home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
v10n01.arc
/
PCSPOOL.ASM
< prev
next >
Wrap
Assembly Source File
|
1990-12-11
|
130KB
|
4,001 lines
page 55,132
title "PCSPOOL - Print Spooler"
svc_charq equ 500/18 ; dflt chars per service
svc_testq equ 500 ; dflt nbr of tests per char
lpt_dqsz equ 1024 ; deque buffer size
; --------------------------------------------
; definition of structure for a single printer
; --------------------------------------------
lpstruc struc
lpprtnbr dw ? ; printer number
lpstatport dw 0 ; printer status port addr
lpfirstqr dw 0 ; first record on queue
lplastqr dw 0 ; last record on queue
lpprted dd 0 ; characters actually prt'd
lpinque dd 0 ; characters in spool
lpdqbuf dd 0 ; pointer to dq buf (puddle)
lplast dd 0 ; last cnt of prt'd chars
lpticks dd 0 ; nbr ticks for prt'd chrs
lpdqin dw 0 ; offset of next char to put
; .. updated by main
lpdqout dw 0 ; offset of next char to prt
; .. updated by toprt
lpwrkqr dw 0 ; pointer to spooling qrec
lpspools db 90h ; current spooling status
lpstatus db 0feh ; current printer status
; 00 = ok
; 01 = not ready (hardware)
; 02 = paused w/message
; 03 = paused
; 04 = waiting init'ing
; fe = non-existent port
; ff = not currently spool
lpcontrol db 0 ; current ctrl record type
lpcps dw 0 ; observed printer speed
lpchars dw svc_charq ; chars to send per service
lpwrite db 0 ; 1 = write need
lpflush db 0 ; 1 = flush all queued data
lptmrflg db 0 ; 1 = update cps stats
lpstruc ends
; --------------------------------
; definition of queue record entry
; --------------------------------
qrlen equ 512 ; length of a qrec entry
; note: a change from 512
; requires code change
; in getmemz routine
qrdlen equ qrlen - 5 ; data length
qrstruc struc
qrnextqr dw ? ; next record on queue
qrcontrol db ? ; control byte
; 00: data
; 01: data, next rec is ctl
; 02: display qrdata, pause
; 04: initialize printer
qrbytes dw ? ; bytes in record
qrdata db qrdlen dup (?) ; remainder is data
qrstruc ends
cgroup group mainline, convq, diskq
mainline segment para public 'code' ; mainline
assume cs:mainline, ds:mainline, es:mainline, ss:mainline
org 100h
begin: jmp start ; start processing
; --------------------------------------------------------
; This is the area where the queue header will be loaded
; --------------------------------------------------------
qheader label byte ; q headers area
qhvalid dw 0 ; signature byte
qhfirstf dw 0 ; first free buffer
qhlpt1 lpstruc <0> ; structure for lpt1
qhlpt2 lpstruc <1> ; structure for lpt2
qhlpt3 lpstruc <2> ; structure for lpt3
lplen equ qhlpt2 - qhlpt1 ; length of lp structure
qhlen equ 2+(lplen*3) ; queue header length
; --------------------------
; interrupt vector control
; --------------------------
intstksz equ 32 ;; debug ;; ; size of interrupt stacks
intblkcnt equ 6 ; number of int routines
intblklen equ 15 ; length of blocks
intblk1 equ offset oldint13 ; first to fill in
oldint13 dd 0 ; old cs:ip
oldss13 dd 0 ; ss:sp when int invoked
newss13 dd 0 ; work stack during int
db 13h ; interrupt to take over
dw offset int13 ; new interrupt offset
oldint09 dd 0 ; old cs:ip
oldss09 dd 0 ; ss:sp when int invoked
newss09 dd 0 ; work stack during int
db 09h ; interrupt to take over
dw offset int09 ; new interrupt offset
oldint17 dd 0 ; old cs:ip
oldss17 dd 0 ; ss:sp when int invoked
newss17 dd 0 ; work stack during int
db 17h ; interrupt to take over
dw offset int17 ; new interrupt offset
oldint08 dd 0 ; old cs:ip
oldss08 dd 0 ; ss:sp when int invoked
newss08 dd 0 ; work stack during int
db 08h ; interrupt to take over
dw offset int08 ; new interrupt offset
oldint21 dd 0 ; old cs:ip
oldss21 dd 0 ; ss:sp when int invoked
newss21 dd 0 ; work stack during int
db 21h ; interrupt to take over
dw offset int21 ; new interrupt offset
oldint28 dd 0 ; old cs:ip
oldss28 dd 0 ; ss:sp when int invoked
newss28 dd 0 ; work stack during int
db 28h ; interrupt to take over
dw offset int28 ; new interrupt offset
svc_chars dw svc_charq ; chars per service
pcsflg db 0 ; flags
pcsflgl1 equ 01h ; .... ...1 init: spool lpt1
pcsflgl2 equ 02h ; .... ..1. init: spool lpt2
pcsflgl3 equ 04h ; .... .1.. init: spool lpt3
pcsflgcm equ 08h ; .... 1... conv memory
pcsflgdk equ 10h ; ...1 .... disk spooling
pcsflgem equ 20h ; ..1. .... ems (future)
pcsflgsx equ 38h ; all bits for spool type
qrtn dd 0 ; queue routine
qrtnlen dw 0 ; .. len of routine
qrtnseg equ word ptr qrtn+2 ; .. segment of queue rtn
dqrec dw 0 ; ptr to work dq record
qfile db "C:\" ; default drive/directory
db 65 dup (0)
nextqrnbr dw 1 ; next queue record nbr
memqseg dw 0 ; segment of memory spool
memqsize dw 16 ; K size (init) recs (run)
nxtavail dw qhandler ; next available byte
startclr dw 0 ; addr to start mem clear
clrlen dw 0 ; length to clear
dos_busy dd 0 ; addr of dos busy flag
i28hflag db 0 ; used by DOS Waiting rtn
intsbusy db 0 ; interrupts busy flag
i08hflg dw 0 ; int 08 in use
i17hflg db 0 ; int 17 in use (part)
chkprtflg db 0 ; chkprt rtn in use
qinuseflg db 0 ; queue in use flag
close_req db 0 ; close requested flag
; 0 = not requested
; 1 = close requested
; 2 = close completed
i21hufh dw 0 ; user's file handle
i21hlen dw 0 ; remaining length
i21hflg db 1 ; in use flag
tsr_active db 0 ; tsr active flag
tsr_req db 0 ; tsr request flag
tsr_cnt db 0 ; tsr tick counter
db "Hot key>" ; visual ptr for hot key
shift_mask db 0ch ; ..and shift mask
; .... ...1 right shift
; .... ..1. left shift
; .... .1.. control key
; .... 1... alt key
hot_key db 19h ; scan code of hotkey
; ---------------------------------------------------------------------
; screen management items
; ---------------------------------------------------------------------
lines equ 5 ; 5 lines
cols equ 80 ; .. of 80 columns
monoattr equ 07h ; monochrome attribute
colattr equ 1fh ; color attribute
ourattr db 07h ; attr to use, default mono
; display status messages
; (lpstatus, length, msg)
dstat_msgs db 0, 43, ' xxx% ********** sssK nnnK mmmK hh:mm:ss'
db 1, 18, ' Printer not ready'
db 2, 9, ' Paused: '
db 3, 27, ' Paused, use GO to continue'
db 4, 23, ' Waiting initialization'
db 5, 15, ' Flushing queue'
db 0feh, 17, ' No printer found'
db 22, ' Not currently spooled'
dstat_nbr equ 7 ; nbr of messages - 1
; --------------------------------------------------------------------
; This routine checks the buffer for the printers and sends the info
; out via the original INT17 routine
; --------------------------------------------------------------------
toprt proc ; send to printer
push ax ; save registers
push bx
push cx
push dx
push si
push ds
push es
push cs ; save our segment addr
pop ds ; ds -> our segment
mov bx, offset qhlpt1 ; bx -> lpt1 control block
toprt10: cmp [bx].lpstatus, 01h ; q. available for print?
jbe toprt15 ; a. yes .. do something
cmp [bx].lpstatus, 4 ; q. need initialization?
je toprt12 ; a. yes .. handle w/BIOS
jmp toprt80 ; else .. do next printer
toprt12: mov ah, 1 ; ah = init printer fnc
mov dx, [bx].lpprtnbr ; dx -> printer nbr
pushf ; .. save our flags
call oldint17 ; call old int 17 routine
mov [bx].lpstatus, 0 ; make printer "ready"
mov [bx].lpcontrol, 0 ; ..for next queue record
jmp short toprt80 ; ..continue w/common code
toprt15: mov ax, [bx].lpdqin ; ax = input offset
cmp ax, [bx].lpdqout ; q. anything to print?
je toprt80 ; a. no .. try again later..
mov ax, svc_chars ; ax = chars per service
mov [bx].lpchars, ax ; .. reset chars this svc
toprt20: mov cx, svc_testq ; cx = nbr of times to try
mov dx, [bx].lpstatport ; dx = status port addr
toprt22: in al,dx ; pre-charge +busy line (?)
in al,dx ; al = prt port status
test al, 10h ; q. printer selected?
jnz toprt24 ; a. yes .. check for ready
mov [bx].lpstatus, 1 ; else .. hardware problem
jmp short toprt80 ; .. check next printer
toprt24: mov [bx].lptmrflg, 1 ; increment CPS stats
test al, 80h ; q. printer ready?
jnz toprt25 ; a. yes.. print a char
loop toprt22 ; else .. try again
jmp short toprt80 ; done trying? .. nxt prt
toprt25: mov dx, [bx].lpprtnbr ; dx -> printer nbr
mov [bx].lpstatus, 0 ; show printer available
push bx ; save lp ptr
xor ah, ah ; ah = print a char
les si, [bx].lpdqbuf ; es:si -> memory puddle
mov bx, [bx].lpdqout ; bx -> next char to print
mov al, es:[bx+si] ; al = char to print
inc bx ; bx -> next char
cmp bx, lpt_dqsz ; q. bx beyond max?
jb toprt30 ; a. no .. continue
xor bx, bx ; else .. reset pointer
toprt30: mov cx, bx ; cx = new offset
pop bx ; bx -> lpt block
mov [bx].lpdqout, cx ; save new buffer offset
pushf ; .. save our flags
call oldint17 ; call old int 17 routine
add word ptr [bx].lpprted, 1 ; increment printed cnt
adc word ptr [bx].lpprted+2, 0
cli ; hold interrupts
sub word ptr [bx].lpinque, 1 ; dec chars left
sbb word ptr [bx].lpinque+2, 0 ; . . . . .
sti ; re-enable ints
cmp [bx].lpdqin, cx ; q. any chars to print?
je toprt80 ; a. no .. exit
dec [bx].lpchars ; q. enough chars sent?
jnz toprt20 ; a. no .. send some more
toprt80: add bx, lplen ; bx -> next lp block entry
cmp bx, offset qhlpt3 ; q. last printer serviced?
ja toprt90 ; a. yes .. exit loop
jmp toprt10 ; else .. do another printer
toprt90: pop es ; restore registers
pop ds
pop si
pop dx
pop cx
pop bx
pop ax
ret ; return to caller
toprt endp
; ----------------------------------------------------------------------
; This routine handles the users attempt to print using BIOS interrupt
; 17h. Output is queued to the printer if ready, else buffered then
; spooled.
;
; Standard int 17 Calls
; ---------------------
;
; Send Character to Printer
; ah = 00h, funtion code
; al = character to print
; dx = printer number
;
; Returns
; ah = printer status
;
;
; Initialize Printer
; ah = 01h, funtion code
; dx = printer number
;
; Returns
; ah = printer status
;
;
; Get Printer Status
; ah = 02h, funtion code
; dx = printer number
;
; Returns
; ah = printer status
; 01h, printer busy
; 90h, printer ready
;
;
; Extended int 17 Calls
; ---------------------
;
; Get Control Block Address
; ah = 0c0h, funtion code
; dx = printer number
;
; Returns
; es:bx -> control block
;
;
; Build Control Record (Flushes pending writes)
; ah = 0c1h, funtion code
; dx = printer number
; ds:si -> ASCIIZ string to save for display
;
;
; Flush Pending Writes
; ah = 0c2h, funtion code
; dx = printer number
;
;
; Cancel Printer Queue (Flush all queued output)
; ah = 0c3h, funtion code
; dx = printer number
;
;
; Query Spooler Active
; ah = 0c4h, function code
;
; Returns
; di = 0b0bfh
; si = segment
;
;
; Job Skip Printer Queue (Cancels upto the pause record)
; ah = 0c5h, funtion code
; dx = printer number
;
;
; Check Printer Queue Status
; ah = 0c6h, funtion code
; dx = printer number
;
; Returns
; ax = 0 = printer not active or at pause
; 1 = printer busy
;
;
; Close Queue
; ah = 0c7h, funtion code
; dx = printer number (0)
;
;
; ----------------------------------------------------------------------
int17tbl label byte ; funtion code/routine table
db 00h ; print a character
dw i17h40
db 01h ; initialize printer
dw i17h45
db 02h ; get printer status
dw i17h50
db 0c0h ; get control block addr
dw i17h55
db 0c1h ; build control record
dw i17h60
db 0c2h ; flush pending writes
dw i17h65
db 0c3h ; cancel printer queue
dw i17h70
db 0c5h ; job skip queue
dw i17h80
db 0c6h ; check printer status
dw i17h85
db 0c7h ; set close queue flag
dw i17h89
db 0ffh ; end of list marker
dw i17h50 ; ..default get printer stat
i17hcnt db 0 ; int 17 counter
int17 proc ; spool to printer interrupt
cmp ah, 0c4h ; q. spooler active request?
jne i17h05 ; a. no .. continue
mov di, 0b0bfh ; move in signature bytes
mov si, cs ; ..and our segment
iret ; ..return to caller
i17h05: push ds ; save registers
push bx ;
push cs ; get addressibility
pop ds ; . . . .
mov bx, offset qhlpt1 ; bx -> lpt1 control block
i17h10: cmp dx, [bx].lpprtnbr ; q. right printer?
je i17h15 ; a. yes .. check if spool'g
add bx, lplen ; bx -> next printer block
cmp bx, offset qhlpt3 ; q. last printer?
jna i17h10 ; a. no .. try again
jmp short i17h19 ; else .. old int 17 rtn
i17h15: cmp [bx].lpstatus, 0feh ; q. being spooled?
jb i17h20 ; a. yes .. continue
i17h19: cmp ah, 0c0h ; q. one of ours?
jb i17h19b ; a. no .. goto old int 17
cmp ah, 0c7h ; q. still one of ours?
ja i17h19b ; a. no .. goto old int 17
cmp ah, 0c0h ; q. get printer ctrl blk?
je i17h20 ; a. yes .. continue
cmp ah, 0c6h ; q. check printer?
jne i17h19a ; a. no .. return to caller
xor ax, ax ; ax = 0, printer not busy
i17h19a: pop bx ; restore registers
pop ds ;
iret ; ..then return to caller
i17h19b: pop bx ; restore registers
pop ds ;
jmp cs:oldint17 ; then goto old int 17 rtn
; ..and rtn directly to user
i17h20: mov i17hcnt, 1 ; show int 17 called
push ax ; save rest of registers
push si ;
mov si, offset int17tbl ; si -> function/routine tbl
i17h25: cmp ah, byte ptr [si] ; q. do we point at this fnc
je i17h30 ; a. yes .. exit loop
add si, 3 ; si -> next list entry
cmp byte ptr [si], 0ffh ; q. end of the list?
jne i17h25 ; a. no .. loop some more
i17h30: jmp word ptr 1[si] ; ..goto the proper routine
; --------------------------
i17h40 label byte ; print a character
cmp [bx].lpwrite, 1 ; q. write needed?
jne i17h401 ; a. no .. go ahead
call writeqr ; q. able to send qr away?
jnc i17h401 ; a. yes .. continue
jmp i17h90 ; else .. return w/busy
i17h401: cli ; stop ints for a second
add word ptr [bx].lpinque, 1 ; increment total in que
adc word ptr [bx].lpinque+2, 0
sti ; restart ints
push bx ; save printer ctrl blk ptr
mov si, [bx].lpwrkqr ; si -> work queue record
inc i17hflg ; critical process active
mov bx, [si].qrbytes ; bx = nbr of bytes in rec
mov [si+bx].qrdata, al ; store character in que rec
inc [si].qrbytes ; bump character counter
dec i17hflg ; critical process now done
pop bx ; restore register
cmp [si].qrbytes, qrdlen ; q. queue record filled?
je i17h402 ; a. yes .. move it
jmp i17h90 ; else, just return
i17h402: mov [si].qrcontrol, 0 ; make record type be data
call writeqr ; q. able to send rec away?
jc i17h403 ; a. no .. setup for later
jmp i17h90 ; else .. rtn to caller ok
i17h403: mov [bx].lpspools, 90h ; show printer still ready
mov [bx].lpwrite, 1 ; ..and write needed
jmp i17h90 ; ..then return to caller
; --------------------------
i17h45 label byte ; initialize printer
mov si, [bx].lpwrkqr ; si -> work queue record
mov [si].qrcontrol, 1 ; show next record is ctrl
call writeqr ; send queue record away
mov [si].qrcontrol, 4 ; record is an init. record
call writeqr ; send queue record away
jmp i17h90 ; ..then return to caller
; --------------------------
i17h50 label byte ; get printer status
mov si, [bx].lpwrkqr ; si -> work queue record
jmp i17h90 ; ..in exit routine
; --------------------------
i17h55 label byte ; get printer control block
mov ax, ds ; ax = our data segment
mov es, ax ; es -> our data segment
pop si ; restore registers
pop ax ;
add sp, 2 ; skip restoring es:bx
pop ds ;
iret ; return, es:bx -> prt blk
; --------------------------
i17h60 label byte ; build control record
push bp ; save bp register
mov bp, sp ; get stack base pointer
push di ; save some more registers
push si
push cx
push es
push ds
mov si, [bx].lpwrkqr ; si -> work queue record
push si ; save record pointer
mov [si].qrcontrol, 1 ; show next record is ctrl
call writeqr ; send queue record away
mov [si].qrcontrol, 2 ; rec type = pause w/msg
lea di, [si].qrdata + 2 ; di -> string area
push ds ; make ..
pop es ; es -> our segment
mov si, [bp]+2 ; si -> user's string
mov ds, [bp]+8 ; ds:si -> asciiz string
xor cx, cx ; cx = character counter
i17h62: cmp cx, qrdlen-3 ; q. enough moved?
jb i17h63 ; a. yes .. move no more
xor al, al ; simulate end
jmp short i17h64 ; .. store a null
i17h63: lodsb ; al = character from user
i17h64: stosb ; ..store in the queue rec
inc cx ; cx = copied count
or al, al ; q. last character?
jnz i17h62 ; a. no .. loop
cmp cx, 35 ; q. will message scroll?
jna i17h64a ; a. no .. skip extra char
mov byte ptr es:[di-1],'' ; save end of message char
inc cx ; .. and count in length
i17h64a: inc cx ; cx = nbr chars + length
pop si ; si -> queue record
pop ds ; restore our data segment
mov [si].qrbytes, cx ; save byte count
sub cx, 2 ; cx = string length
mov word ptr [si].qrdata, cx ; save in message
pop es ; restore rest of the regs
pop cx ;
pop si ;
pop di ;
pop bp ;
call writeqr ; send queue record away
jmp short i17h90 ; ..then exit routine
; --------------------------
i17h65 label byte ; flush pending writes
mov bx, offset qhlpt1 ; bx -> lpt1 control block
i17h67: mov si, [bx].lpwrkqr ; si -> work queue record
cmp [si].qrbytes, 0 ; q. anything there?
jne i17h68 ; a. no .. next printer
call writeqr ; else .. send queue record
i17h68: add bx, lplen ; bx -> next printer block
cmp bx, offset qhlpt3 ; q. last printer?
jna i17h67 ; a. no .. try again
jmp short i17h90 ; ..in exit routine
; --------------------------
i17h70 label byte ; cancel printer queue
mov [bx].lpflush, 1 ; show queue needs clearing
jmp short i17h82 ; ..and jump into common code
; --------------------------
i17h80 label byte ; job skip printer queue
mov [bx].lpflush, 2 ; show which queue to skip
i17h82: mov si, [bx].lpwrkqr ; si -> work queue record
mov ax, [si].qrbytes ; ax = what was in qrec
cli ; stop ints for a second
sub word ptr [bx].lpinque, ax ; decrement total in que
sbb word ptr [bx].lpinque+2, 0 ; . . .
sti ; restart ints
mov [si].qrbytes, 0 ; clear queue record
mov [si].qrcontrol, 0 ; ..and control byte
mov [bx].lpstatus, 5 ; show waiting flush
; --------------------------
i17h90: pop si ; normal calls return
pop ax ;
mov ah, [bx].lpspools ; return spool status
pop bx ;
pop ds ;
iret ; return to caller
; --------------------------
i17h85 label byte ; check printer queue status
push cx ; save another register
xor ax, ax ; ax = 0, for quiesced prt
cmp [bx].lpstatus, 2 ; q. waiting for pause?
jae i17h86 ; a. yes .. ready
cmp [bx].lpfirstqr, 0 ; q. queue empty?
jnz i17h87 ; a. no .. return now
i17h86: mov cx, [bx].lpdqin ; cx -> nxt input going byte
cmp cx, [bx].lpdqout ; q. empty printer puddle?
je i17h88 ; a. yes ..
i17h87: inc ax ; ax = 1, printer busy
i17h88: pop cx ; return to caller
pop si ;
add sp, 2 ;
pop bx ;
pop ds ;
iret ; return to caller
; --------------------------
i17h89 label byte ; set close print queue flag
mov close_req, 1 ; setup close request flag
jmp short i17h90 ; ..then exit
int17 endp
; ----------------------------------------------------------------------
; This routine will try to copy the queue data to the printer's memory
; puddle, but if that is not ready, the data will be written to the
; spool.
;
; Entry
; bx -> printer control block
;
; Returns
; carry = DOS busy, try later
;
; ----------------------------------------------------------------------
writeqr proc
push si ; save registers
push dx ;
mov si, [bx].lpwrkqr ; si -> queue record
cmp [si].qrcontrol, 1 ; q. control record?
ja writeqr10 ; a. yes .. write it out
cmp [bx].lpcontrol, 0 ; q. draining printer?
jne writeqr10 ; a. yes .. write to queue
call movetoprt ; move spool record to prt
jnc writeqr30 ; ..everything ok, continue
writeqr10: call usingdos ; q. DOS available?
jc writeqr25 ; a. no .. try later
writeqr20: mov dx, si ; dx -> work queue record
mov ax, [bx].lpprtnbr ; ax = printer number
mov ah, 1 ; ah = write queue record
call qh ; q. write the record ok?
jnc writeqr30 ; a. yes .. continue
writeqr25: mov [bx].lpspools, 01h ; show printer busy
mov [bx].lpwrite, 1 ; show a write is needed
pop dx ; restore registers
pop si ;
stc ; set carry flag (problems)
ret ; ..and return to caller
writeqr30: mov [si].qrcontrol, 0 ; clear control byte
mov [si].qrbytes, 0 ; ..character count
mov [bx].lpwrite, 0 ; ..write needed flag
mov [bx].lpspools, 90h ; show printer ready, again
pop dx ; restore registers
pop si ;
clc ; clear carry flag
ret ; ..and rtn to caller, "ok"
writeqr endp
; ----------------------------------------------------------------------
; This routine will try to move the current queue record to the
; de-queuing routines memory puddle. This helps cut down on spool
; i/o when the printer is keeping up with the application program.
;
; Entry
; bx -> printer control block
;
; Returns
; carry = printer buffer not empty enough
;
; ----------------------------------------------------------------------
movetoprt proc
cmp [bx].lpfirstqr, 0 ; q. any records in chain?
jne movetop10 ; a. yes .. move to spool
push si ; save register
push cx
call getprt ; get available prt space
mov si, [bx].lpwrkqr ; si -> work queue record
cmp [si].qrbytes, cx ; q. enough room in memory?
jbe movetop20 ; a. yes .. move in the rec
pop cx ; restore register
pop si ;
movetop10: stc ; ..set carry flag
ret ; ..then rtn w/can't do stat
movetop20: call moverecord ; move record to puddle
pop cx ; restore regs
pop si
clc ; clear carry .. show "ok"
ret ; ..then return to caller
movetoprt endp
; ----------------------------------------------------------------------
; This routine will return the availability of DOS for file i/o.
; If the spool is not using DOS file i/o (using conventional or EMS
; memory) then the return will always be "ok"
;
; Returns
; carry = DOS not ready for file i/o
;
; ----------------------------------------------------------------------
usingdos proc
test pcsflg, pcsflgdk ; q. are we using disk queue
jz usingdos90 ; a. no .. return ok
cmp i28hflag, 0 ; q. called from DOS Waiting
jne usingdos90 ; a. yes .. then we're ok
cmp intsbusy, 0 ; q. one of interrupts busy?
jne usingdos80 ; a. yes .. exit w/wait code
push es ; save registers
push bx ; ..
les bx, dos_busy ; es:bx -> DOS busy flag
cmp byte ptr es:[bx], 0 ; q. DOS busy
pop bx ; ...restore registers
pop es ; ...
je usingdos90 ; a. no .. return to user
usingdos80: stc ; else .. show DOS is busy
ret ; ..and return to caller
usingdos90: clc ; clear carry .. a-ok
ret ; ..and return to caller
usingdos endp
; ----------------------------------------------------------------------
; This routine moves a record to the printer memory puddle and
; adjusts the pointers.
;
; Entry
; bx -> printer control block
; si -> queue record to move
;
; ----------------------------------------------------------------------
moverecord proc
push ax ; save registers
push cx
push di
push si
push es
cld ; clear direction flag
mov di, [bx].lpdqin ; di -> input offset
mov es, word ptr [bx].lpdqbuf+2 ; es = segment of puddle
mov cx, lpt_dqsz ; cx -> end of puddle
sub cx, di ; cx = max chars in 1st copy
mov al, [si].qrcontrol ; al = record type
mov [bx].lpcontrol, al ; save record type
cmp al, 1 ; q. end of data record?
jne moverec20 ; a. no .. continue
xor al, al ; al = new record type
moverec20: mov [bx].lpstatus, al ; setup new status
mov ax, [si].qrbytes ; ax = nbr of chars in rec
mov [si].qrbytes, 0 ; then clear count
mov [si].qrcontrol,0 ; ..and record type
lea si, [si].qrdata ; si -> queue record data
or ax, ax ; q. anything to move?
jz moverec90 ; a. no .. better exit
cmp ax, cx ; q. enough room?
ja moverec30 ; a. no .. need two..
mov cx, ax ; cx = amount to move
jmp short moverec40 ; goto common code
moverec30: push cx ; save 1st portion length
rep movsb ; move 1st portion of data
pop cx ; cx = 1st portion length
sub ax, cx ; ax = get new length ..
mov cx, ax ; cx = setup for rep/movsb
xor di, di ; di -> start of puddle
moverec40:
rep movsb ; move queue data to puddle
cmp [bx].lpstatus, 2 ; q. pause message?
je moverec90 ; a. yes .. data doesn't count
cmp di, lpt_dqsz ; q. at end of puddle?
jb moverec50 ; a. no .. save as is
xor di, di ; di -> first byte of pud'l
moverec50: mov [bx].lpdqin, di ; store new end pointer
moverec90: pop es ; restore registers
pop si
pop di
pop cx
pop ax
ret ; ..then return to caller
moverecord endp
; ----------------------------------------------------------------------
; This routine interrupts writes using function 40h to any printer
; handles. Writes are parcel'd out to the max of the requested write
; or the remainding size of the spooler's available buffer. If the
; handle did use the spooler than the remainder of the write request
; is send in chunks. This will allow the disk write routines enough
; of a breath to write their buffers to disk.
;
; ----------------------------------------------------------------------
int21 proc ; interrupt 21 handler
cmp cs:i21hflg, 0 ; q. int 21 in use?
jz i21h10 ; a. no .. check it out
jmp cs:oldint21 ; ..then goto DOS
i21h10: inc cs:i21hflg ; mark that we are here
call chkprt ; check for any reads/writes
cmp ah, 40h ; q. write to file handle?
je i21h15 ; a. yes .. check it out
jmp short i21h18 ; else .. goto DOS
i21h15: push dx ; save buffer address
push ax ; .. and function
mov ax, 4400h ; ax = IOCTL/Get Dev Info
pushf ; emulate an interrupt
call cs:oldint21 ; call DOS
cmp al, 0c0h ; q. printer, maybe?
pop ax ; .. restore ax
pop dx ; .. restore dx
je i21h20 ; a. yes .. continue
i21h18: dec cs:i21hflg ; clear in use flag
jmp cs:oldint21 ; ..then goto DOS
i21h20: push bx ; save registers
push dx
push cx
mov cs:i17hcnt, 0 ; clear int 17 used flag
mov cs:i21hufh, bx ; save file handle
mov cs:i21hlen, cx ; ..remaining length
i21h30: cli ; disallow ints
call chkprt ; write any full buffers
call getmax ; find max writable length
sti ; let int's happen
or bx, bx ; q. anyplace to write yet?
jz i21h30 ; a. no .. try again
cli
cmp bx, cx ; q. plenty of room?
jae i21h40 ; a. yes .. continue
mov cx, bx ; cx = length to write
i21h40: mov ah, 40h ; ah = write to handle
mov bx, cs:i21hufh ; bx = original handle
pushf ; emulate an interrupt
call cs:oldint21 ; q. error writing?
jnc i21h50 ; a. no .. continue
pop cx ; restore register
jmp short i21h70 ; ..and exit thru bottom
i21h50: mov cx, cs:i21hlen ; cx = what was left
or ax, ax ; q. anything print?
jz i21h60 ; a. no .. better exit
sub cx, ax ; q. anything left?
jz i21h60 ; a. no .. exit loop
mov cs:i21hlen, cx ; save remaining length
add dx, ax ; dx -> next block full
cmp cs:i17hcnt, 0 ; q. int 17 used?
jne i21h30 ; a. yes .. loop back
jmp i21h40 ; else .. finish up
i21h60: pop cx ; restore registers
mov ax, cx ; ax = written count
clc ; show everything ok..
i21h70: pop dx ; restore the rest
pop bx
dec cs:i21hflg ; clear int 21 use flag
iret ; ..and return to caller
int21 endp
; ----------------------------------------------------------------------
; This routine checks for queue reads/writes which need to be done,
; then calls the queue management routines to handle them.
;
; ----------------------------------------------------------------------
chkprt proc
cmp cs:chkprtflg, 0 ; q. chkprt in use?
jne chkprt0 ; a. yes.. exit
cmp cs:qinuseflg, 0 ; q. qh in use?
je chkprt00 ; a. no .. continue
chkprt0: ret ; else .. return to caller
chkprt00: inc cs:chkprtflg ; show we're in routine
push ax ; save registers
push bx
push cx
push dx
push si
push ds
push cs ; get addressibility
pop ds ; . . . .
cmp close_req, 1 ; q. need to close queue?
jne chkprt05 ; a. no .. continue
mov ax, 7 ; ax = close spool function
call dword ptr qrtn ; call queue routine
mov close_req, 2 ; show queue closed
jmp chkprt90 ; ..then return to caller
chkprt05: mov bx, offset qhlpt1 ; bx -> lpt1 control block
chkprt10: cmp [bx].lpflush, 0 ; q. need to flush queue?
je chkprt40 ; a. no .. continue
mov cl, [bx].lpflush ; cl = flush mode
mov dx, dqrec ; dx -> deque record
mov si, dx ; si -> same record
chkprt20: mov ax, [bx].lpprtnbr ; ah = read, al = prt nbr
call qh ; q. get the next qrecord?
jc chkprt30 ; a. no .. try later
cmp [si].qrcontrol, 1 ; q. data record?
ja chkprt25 ; a. no .. continue
mov ax, [si].qrbytes ; ax = nbr bytes in record
sub word ptr [bx].lpinque, ax ; subtract out this rec
sbb word ptr [bx].lpinque+2, 0 ; . . .
chkprt25: cmp cl, 1 ; q. flush entire queue
je chkprt20 ; a. yes .. get next qrec
cmp [si].qrcontrol, 1 ; q. next rec a pause?
jne chkprt20 ; a. no .. get next qrec
chkprt30: mov [bx].lpflush, 0 ; clear flush flag
mov [bx].lpcontrol, 0 ; ..and last control rec
chkprt40: cmp [bx].lpwrite, 1 ; q. write needed?
jne chkprt42 ; a. no .. check for reads
call writeqr ; send qr away..
chkprt42: cmp [bx].lpstatus, 1 ; q. printer printing?
ja chkprt70 ; a. no .. next!
call getprt ; ax = max puddle space
cmp [bx].lpcontrol, 0 ; q. next qrec data?
je chkprt45 ; a. yes .. get it
cmp cx, lpt_dqsz - 1 ; q. buffer empty?
jne chkprt70 ; a. no .. wait till done
xor ax, ax ; ax = 0, for convience
mov [bx].lpdqin, ax ; clear input
mov [bx].lpdqout, ax ; ..and output pointers
chkprt45: cmp cx, qrlen ; q. enough for a read?
jb chkprt70 ; a. no .. next printer
mov ax, [bx].lpprtnbr ; ah = read, al = prt nbr
mov dx, dqrec ; dx -> deque record
call qh ; q. get the next qrecord?
jc chkprt50 ; a. no .. try later
mov si, dx ; si -> record
jmp short chkprt60 ; ..continue w/common code
chkprt50: mov si, [bx].lpwrkqr ; si -> in core wrk record
cmp i17hflg, 0 ; q. anybody do'in int 17?
jnz chkprt70 ; a. yes .. wait till later
chkprt60: call moverecord ; move record to prt puddle
chkprt70: add bx, lplen ; bx -> next printer block
cmp bx, offset qhlpt3 ; q. last printer?
ja chkprt90 ; a. yes .. return to caller
jmp chkprt10 ; else .. next printer
chkprt90: pop ds ; restore register
pop si
pop dx
pop cx
pop bx
pop ax
dec cs:chkprtflg ; clear rtn in use flag
ret ; ..and return to caller
chkprt endp
; ----------------------------------------------------------------------
; The routine returns the maximun write which the spooling system can
; withstand by DOS at this moment. This is acually the smallest
; available buffer space.
;
; Returns
; bx = maximum available space in work qrecords
;
; ----------------------------------------------------------------------
getmax proc
push ax ; save registers
push cx
push si
push ds
push cs ; get addressibility
pop ds
mov ax, qrdlen ; ax = max value
mov bx, offset qhlpt1 ; bx -> lpt1 control block
getmax10: mov si, [bx].lpwrkqr ; si -> int 17's queue rec
mov cx, qrdlen ; cx = max record size
sub cx, [si].qrbytes ; cx = remaining free bytes
cmp ax, cx ; q. this printer less?
jb getmax20 ; a. no .. get next printer
mov ax, cx ; ax = new lesser value
getmax20: add bx, lplen ; bx -> next printer block
cmp bx, offset qhlpt3 ; q. last printer?
jna getmax10 ; a. no .. try again
mov bx, ax ; bx = return value
pop ds ; restore registers
pop si ;
pop cx ;
pop ax ;
ret ; return to caller
getmax endp
; ----------------------------------------------------------------------
; The routine returns the available area in this printer's memory
; puddle.
;
; Entry
; bx -> printer control block
;
; Returns
; cx = maximum available space in memory puddle
;
; ----------------------------------------------------------------------
getprt proc
push ax ; save registers
mov ax, [bx].lpdqin ; ax -> next incoming char
mov cx, [bx].lpdqout ; cx -> next printed char
cmp ax, cx ; q. buffer empty?
je getprt10 ; a. yes .. use max
jb getprt20 ; almost full .. any left?
sub ax, cx ; ax = what's used
mov cx, (lpt_dqsz - 1) ; cx = max available
sub cx, ax ; cx = what's not used
pop ax ; restore register
ret ; ..and return to caller
getprt10: mov cx, (lpt_dqsz - 1) ; cx = puddle size
pop ax ; restore register
ret ; ..and return to caller
getprt20: sub cx, ax ; cx = what's left
dec cx ; ..minus 1
pop ax ; restore register
ret ; ..and return to caller
getprt endp
; ----------------------------------------------------------------------
; This routine checks the printers memory puddles for needing
; refilling, and checks the "write needed" flags on the spooling side.
; ----------------------------------------------------------------------
int28 proc
sti ; enable interrupts
inc cs:i28hflag ; show we're in int 28 rtn
call chkprt ; handle printer puddles
dec cs:i28hflag ; show we're out of int 28
jmp dword ptr cs:oldint28 ; jump to old int 28 routine
int28 endp
; ----------------------------------------------------------------------
; This routine keeps track of the int 13 usage for the DOS-in-use rtn.
; ----------------------------------------------------------------------
int13 proc
sti ; enable interrupts
inc cs:intsbusy ; show interrupt is busy
pushf ; fake interrupt call
call dword ptr cs:oldint13 ; call the old int 13 rtn
dec cs:intsbusy ; now show we're done
iret ; ..and return to caller
int13 endp
; ----------------------------------------------------------------------
; Try to print some characters at timer tick.
; ----------------------------------------------------------------------
int08 proc
push ds ; save registers
push cs ; setup addressibility
pop ds
pushf ; fake interrupt call
call oldint08 ; call old timer stuff
inc i08hflg ; lock out another call
; ..and cnt if busy toprt'g
cmp i08hflg, 1 ; q. already doing stuff?
jne i08h10 ; a. yes .. exit quickly
sti ; enable interrupts
call toprt ; .. try to print
call tocps ; tally cps speed
call usingdos ; q. DOS available/needed?
jc i08h05 ; a. no .. try later
call chkprt ; see if prt chars needed
i08h05: mov i08hflg, 0 ; clear flag for another call
i08h10: inc tsr_cnt ; increment tsr counter
cmp tsr_req, 0 ; q. tsr wanted?
jz i08h30 ; a. no .. try later
cmp tsr_active, 0 ; q. tsr already active?
jne i08h30 ; a. yes .. continue
cmp chkprtflg, 0 ; q. check print active?
jne i08h30 ; a. yes .. continue
inc tsr_active ; lock out another call
sti ; enable interrupts
call usingdos ; q. DOS available/needed?
jc i08h20 ; a. no .. try later
call tsr ; do tsr stuff
i08h20: mov tsr_active, 0 ; clear tsr active flag
i08h30: pop ds ; restore registers
iret ; ..and return to caller
int08 endp
; ----------------------------------------------------------------------
; Intercept the hot key.
; ----------------------------------------------------------------------
int09 proc
cmp cs:tsr_active, 0 ; q. tsr in use?
jne i09h15 ; a. yes .. get out quickly
push ax ; save used register
in al, 60h ; get key scan code
cmp al, cs:hot_key ; q. our hot-key?
je i09h10 ; a. yes .. continue
pop ax ; restore register
jmp short i09h15 ; ..and get out
i09h10: mov ah, 2 ; ah = get shift status fnc
int 16h ; call BIOS
and al, 0fh ; al = 'shift' bits
cmp al, cs:shift_mask ; q. match our combo?
pop ax ; restore register
je i09h20 ; a. yes . continue
i09h15: jmp cs:oldint09 ; else .. do key as normal
i09h20: pushf ; call old int 9
call cs:oldint09 ; ..to finish reading key
sti ; allow interrupts
push ax ; save work register
i09h30: mov ah, 1 ; ah = get status
int 16h ; q. any keys availabl?
jz i09h40 ; a. no .. exit loop
mov ah, 0 ; ah = get key
int 16h ; get the key ..
jmp i09h30 ; ..and loop till kb empty
i09h40: pop ax ; restore register
inc cs:tsr_req ; show tsr request made
iret ; go back where we came from
int09 endp
; ----------------------------------------------------------------------
; This routine keeps track of variables to calculate CPS during
; the TSR display routines.
; ----------------------------------------------------------------------
tocps proc
push ax ; save registers
push bx
push dx
lea bx, qhlpt1 ; bx -> 1st printer block
tocps00: cmp [bx].lptmrflg, 0 ; q. update CPS?
je tocps90 ; a. no.. next printer
tocps10: mov ax, i08hflg ; ax = nbr ticks this cycle
add word ptr [bx].lpticks, ax ; accumulate total ticks
adc word ptr [bx].lpticks+2, 0 ; ..to print this much
mov [bx].lptmrflg, 0 ; reset timer flag
tocps90: add bx, lplen ; bx -> next printer
cmp bx, offset qhlpt3 ; q. past last printer?
jb tocps00 ; a. no .. loop back
pop dx ; restore registers
pop bx
pop ax
ret ; ..and return to caller
tocps endp
; ----------------------------------------------------------------------
; This routine is the TSR user interface portion. It displays current
; status, controls printers and printer queues.
; ----------------------------------------------------------------------
tsr proc
push ax ; save registers
push bx
push cx
push dx
push si
push di
push bp
push es
mov tsr_req, 0 ; clear tsr request flag
mov ah, 0fh ; ah = get current info
int 10h ; call BIOS
push bx ; save current video page
mov ah, 3 ; ah = get cursor position
int 10h ; call BIOS
push dx ; save cursor position
push cx ; ..and cursor mode
mov ah, 1 ; ah = set cursor type
mov cx, 2020h ; cx = no cursor
int 10h ; call BIOS
call swapdisp ; q. swap video pages ok?
jnc tsr20 ; a. yes .. continue
mov ax, 0e07h ; ax = write a beep
int 10h ; call BIOS
jmp short tsr90 ; ..and return to caller
tsr20: mov tsr_cnt, 0 ; clear tick counter
call dstat ; display current status
tsr30: call chkprt ; check printer queues
call dcmds ; handle commands
jc tsr40 ; ..loop till exit requested
cmp tsr_cnt, 04 ; q. time to update screen?
jb tsr30 ; a. no .. just check queues
jmp short tsr20 ; else .. display status info
tsr40: call swapdisp ; restore video pages
tsr90: pop cx ; cx = original mode
mov ah, 1 ; ah = set cursor mode
int 10h ; .. do it
pop dx ; dx = original position
pop bx ; bx = video page nbr
mov ah, 2 ; ah = set positin
int 10h ; .. do it
pop es ; restore registers
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret ; ..and return to caller
tsr endp
; ----------------------------------------------------------------------
; This routine displays each printers current status.
; ----------------------------------------------------------------------
dstat proc
push es ; save register
mov bx, offset qhlpt1 ; bx -> 1st printer
dstat10: mov dl, [bx].lpstatus ; dl = current status
xor di, di ; di = no other message
cmp dl, 2 ; q. paused w/message?
jne dstat15 ; a. no .. continue
mov di, word ptr [bx].lpdqbuf + 2 ; di = seg of message
dstat15: lea si, dstat_msgs ; si -> messages
mov cx, dstat_nbr ; cx = msgs - 1
dstat20: cmp dl, [si] ; q. is this our message
jne dstat30 ; a. no .. next one
inc si ; si -> last message
jmp short dstat40 ; ..continue w/common code
dstat30: mov al, 1[si] ; al = length of message
cbw ; ax = length of message
add ax, 2 ; ax = len of msg block
add si, ax ; si -> next message
loop dstat20 ; q. anymore messages?
dstat40: cmp dl, 0 ; q. use a canned message?
jne dstat50 ; a. yes .. continue
call dmbld ; build the message
dstat50: call dmove ; move message to display
cmp [bx].lpstatus, 2 ; q. paused w/comment?
jne dstat60 ; a. no .. continue
les si, [bx].lpdqbuf ; es:si -> dqbuffer
mov cx, es:[di] ; cx = string length
cmp cx, 35 ; q. comment need to scroll?
jna dstat60 ; a. no .. continue
push ds ; save regs
push es ; save dqbuf seg
pop ds ; ds -> dqbuf
mov ah, ds:[2] ; ah = first char
mov di, 2 ; di -> output area
mov si, 3 ; si -> input area
dec cx ; cx = chars to move
cld ; .. lower to higher
rep movsb ; .. shift the string
mov [si-1], ah ; save shifted char
pop ds ; restore ds
dstat60: add bx, lplen ; bx -> next printer
cmp bx, offset qhlpt3 ; q. done?
jna dstat10 ; a. no .. loop again
pop es ; restore register
ret ; return to caller
dstat endp
; ----------------------------------------------------------------------
; This routine builds the "printer ok" stats message.
;
; Entry
; bx -> printer control block
; si -> message block
; ----------------------------------------------------------------------
dmbld proc
push si ; save registers
push di ;
push es
push bx ; save lpt pointer
mov di, bx ; ..and setup new ptr
mov ax, word ptr [di].lpticks ; dx:ax = nbr of ticks
mov dx, word ptr [di].lpticks+2 ; ..
mov cx, 10 ; cx = multiplier
call mmul ; scale number by 10
mov bx, 182 ; cx:bx = 18.2ticks/sec
xor cx, cx ; ..
call ddiv ; convert ticks to secs
mov cx, dx ; cx:bx = seconds
mov bx, ax ; ..
mov ax, word ptr [di].lpprted ; dx:ax = printed chars
mov dx, word ptr [di].lpprted+2 ; ..
call ddiv ; divide to get char/sec
pop bx ; restore lpt pointer
mov [bx].lpcps, ax ; save observed CPS rating
dmbld20: xor dx, dx ; make into doubleword
mov di, si ; di -> output area
add di, 22 ; di -> output area (lsb)
call dformat ; convert to ascii
mov ax, word ptr [bx].lpprted ; ax = part cnt
mov dx, word ptr [bx].lpprted+2 ; dx:ax = spooled chr
mov di, si ; di -> output area
add di, 28 ; di -> output area (lsb)
call dformat ; convert to ascii
mov ax, word ptr [bx].lpinque ; ax = part cnt
mov dx, word ptr [bx].lpinque+2 ; dx:ax = chars in que
mov di, si ; di -> output area
add di, 34 ; di -> output area (lsb)
call dformat ; convert to ascii
call dgauge ; build gauge & percent
push bx ; save lp pointer
mov ax, word ptr [bx].lpinque ; ax = part cnt
mov dx, word ptr [bx].lpinque+2 ; dx:ax = chrs in que
mov bx, [bx].lpcps ; cx:bx = observed CPS
xor cx, cx ; ..
call ddiv ; get secs left to prt
pop bx ; restore register
mov di, si ; di -> output record
or dx, dx ; q. > 65535 seconds?
jnz dmbld30 ; a. yes .. put up hours
add di, 43 ; di -> output area (lsb)
mov cx, 60 ; cx = divisor
div cx ; ax = hr/min, dx = seconds
call ditoa ; put out seconds
xor dx, dx ; dx = 0 for division
div cx ; ax = hours, dx = minutes
call ditoa ; put out minutes
mov dx, ax ; dx = hours
call ditoa ; put out hours
mov byte ptr [si+38], ':' ; patch up message
mov byte ptr [si+41], ':'
jmp short dmbld40 ; ..continue w/common code
dmbld30: push bx ; save lp pointer
mov bx, 3600 ; cx:bx = seconds per hour
xor cx, cx ; ..
call ddiv ; get hours left to print
pop bx ; restore register
add di, 38 ; di -> output area (lsb)
call dformat ; put out hours
mov word ptr [si+40], 'H ' ; patch up message
mov word ptr [si+42], 'sr' ; with " Hrs"
dmbld40: pop es ; restore registers
pop di ;
pop si ;
ret ; then return to caller
dmbld endp
; ----------------------------------------------------------------------
; This routine prints the message to the screen.
;
; Entry
; bx -> printer control block
; si -> message block
; es:di -> secondary message | 0
; ----------------------------------------------------------------------
dmove proc
push bx ; save registers
push ds
mov ah, 2 ; ah = set cursor pos
mov dh, byte ptr [bx].lpprtnbr ; dh = printer nbr
inc dh ; dh = row number
mov dl, 5 ; dl = column number
mov bh, 0 ; bh = video page nbr
int 10h ; call BIOS
xor ch, ch ; ch = 0
mov cl, [si] ; cl = length
push cx ; save message length
inc si ; si -> message
dmove10: mov ah, 0eh ; ah = write tty style
lodsb ; al = char to write
int 10h ; call BIOS to write char
loop dmove10 ; ..and loop till done
or di, di ; q. any seconday message?
jz dmove20 ; a. no .. continue
mov ds, di ; ds:0 -> rest of the story
xor si, si ; si -> start of buffer
xor di, di ; di = 0, clear flag
lodsw ; ax = length of msg
cmp ax, 34 ; q. greater than 34?
jna dmove15 ; a. no .. leave as is
mov ax, 34 ; .. max it at 42
dmove15: pop cx ; restore 1st part
add cx, ax ; cx = total length
push cx ; save overall length
mov cx, ax ; setup for loop
jmp dmove10 ; ..go back and put out msg
dmove20: pop ax ; ax = last messages count
mov cx, 43 ; cx = max length
sub cx, ax ; q. cx = len to blank out?
jle dmove40 ; a. nah .. nothin to do..
mov ax, 0e20h ; ax = write blanks
dmove30: int 10h ; call BIOS to write char
loop dmove30 ; ..and loop till done
dmove40: pop ds ; restore registers
pop bx
ret ; then return to the caller
dmove endp
; ----------------------------------------------------------------------
; This routine handle the console commands
;
; Returns
; carry = exit tsr requested
; ----------------------------------------------------------------------
dcmds proc
mov ah, 1 ; ah = check for keys fnc
int 16h ; q. anything there?
jnz dcmds10 ; a. yes .. continue
clc ; clear carry
ret ; ..and return to caller
dcmds10: xor ah, ah ; ah = read key fnc
int 16h ; get a key
cmp al, 1bh ; q. escape?
jne dcmds20 ; a. no .. continue
stc ; set carry
ret ; ..and return to caller
dcmds20: or al, al ; q. alt key?
jnz dcmds22 ; a. no .. continue
mov dx, 2 ; dx = lpt3
jmp short dcmds30 ; ..goto common code
dcmds22: cmp al, ' ' ; q. control key?
ja dcmds24 ; a. no .. unshifted
mov dx, 1 ; dx = lpt2
jmp short dcmds30 ; ..goto common code
dcmds24: xor dx, dx ; dx = lpt1
dcmds30: push ax ; save register
mov al, lplen ; al = length of prt block
mul dl ; ax = offset of req prt
add ax, offset qhlpt1 ; ax -> requested prt blk
mov bx, ax ; bx -> lpt block
pop ax ; restore register
cmp [bx].lpstatus, 0feh ; q. non-existant printer?
je dcmds90 ; a. yes .. error
cmp ah, 20h ; q. disable printer?
jne dcmds40 ; a. no .. try next cmd
mov [bx].lpstatus, 0ffh ; set status to not started
mov [bx].lpcontrol, 0ffh ; . . . .
clc ; clear carry
ret ; ..and return to caller
dcmds40: cmp ah, 21h ; q. formfeed printer?
jne dcmds45 ; a. no .. try next cmd
mov ax, 000ch ; ax = write a <ff>
jmp short dcmds95 ; ..goto common code to exit
dcmds45: cmp ah, 24h ; q. job skip?
jne dcmds50 ; a. no .. try next cmd
mov ah, 0c5h ; ah = job skip fnc
int 17h ; call BIOS
jmp short dcmds68 ; then release printer
dcmds50: cmp ah, 19h ; q. pause printer?
jne dcmds55 ; a. no .. try next cmd
cmp [bx].lpstatus, 2 ; q. paused w/comment?
je dcmds53 ; a. yes .. leave it alone
mov [bx].lpstatus, 3 ; setup printer as paused
mov [bx].lpcontrol, 3 ; . . . .
dcmds53: clc ; clear carry
ret ; ..and return to caller
dcmds55: cmp ah, 13h ; q. reset printer?
jne dcmds60 ; a. no .. try next cmd
mov ah, 1 ; ah = init printer
jmp short dcmds95 ; ..goto common code to exit
dcmds60: cmp ah, 2eh ; q. cancel printer?
jne dcmds65 ; a. no .. try next cmd
mov ah, 0c3h ; ah = cancel printer fnc
int 17h ; call BIOS
jmp short dcmds68 ; then release printer
dcmds65: cmp ah, 22h ; q. "go" printer?
jne dcmds90 ; a. no .. try next cmd
dcmds68: mov [bx].lpcontrol, 0 ; make chkprt do a qread
mov [bx].lpstatus, 0 ; restart printer
clc ; clear error flag
ret ; ..ane return to caller
dcmds90: mov ax, 0e07h ; ax = write a beep
int 10h ; call BIOS
clc ; clear carry
ret ; ..and return
dcmds95: int 17h ; call BIOS to do prt fnc
clc ; clear carry
ret ; ..and return
dcmds endp
; ----------------------------------------------------------------------
; This routine formats a doubleword integer into a 4 character
; output field.
;
; Entry
; dx:ax = number to convert
; di -> lsb of output field
;
; Result
; The output field is formatted according to the following rules.
;
; 0 < n < 10000 --> nnnn
; 10001 < n < 999k --> nnnK
; 1m < n --> nnnM
; ----------------------------------------------------------------------
dformat proc
push bx ; save register
mov cx, 4 ; cx = char loop count
or dx, dx ; q. msb = 0?
jnz dform10 ; a. no .. not this range
cmp ax, 10000 ; q. lsb < 10000?
jb dform50 ; a. yes .. don't divide
dform10: cmp dx, 15 ; q. msb portion < 10k
jg dform30 ; a. no .. must be in mils
jl dform20 ; else .. in the thousands
cmp ax, 16960 ; q. lsb portion > 10k
jae dform30 ; a. yes .. setup divisor
dform20: mov bx, 1000 ; cx:bx = 1k divisor
xor cx, cx ; . . .
mov byte ptr [di], "K" ; move in the legend char
jmp short dform40 ; and continue w/common code
dform30: mov bx,16960 ; cx:bx = 1m divisor
mov cx,15 ; . . .
mov byte ptr [di], "M" ; move in the legend char
dform40: call ddiv ; do 32 bit divide here
dec di ; di -> lsb of number
mov cx, 3 ; cx = char loop count
dform50: mov bx, 10 ; setup for itoa()
dform60: div bx ; dx = digit for this place
add dx, '0' ; dl = ascii digit
mov [di], dl ; store in output area
xor dl, dl ; dl = 0 for rest of divides
dec di ; di -> next location
or ax, ax ; q. any more signif digits?
je dform70 ; a. no .. put in blanks
loop dform60 ; else .. loop till done
dform70: dec cx ; q. blanks needed?
jz dform90 ; a. no .. all taken care of
mov dl, ' ' ; dl = BL for lead suppress
dform80: mov [di], dl ; store in output area
dec di ; di -> next location
loop dform80 ; loop lead blanking string
dform90: pop bx ; restore registers
ret ; return to caller
dformat endp
; ----------------------------------------------------------------------
; This routine converts a binary number into two printable ascii
; characters.
;
; Entry
; dx = nbr to convert
; di -> lsb of output
; ----------------------------------------------------------------------
ditoa proc
push ax ; save registers
mov ax, dx ; ax = number
aam ; convert to two packed nbrs
or ax, 3030h ; ax = ascii characters
xchg ah, al ; swap around for store
sub di, 3 ; di -> next lsb
mov word ptr [di+2], ax ; save ascii characters
pop ax ; restore registers
ret ; ..and return to caller
ditoa endp
; ----------------------------------------------------------------------
; This routine determines the percentage of use of the queue area.
;
; Entry
; bx -> printer control block
; si -> message line
; ----------------------------------------------------------------------
dgauge proc
push bx ; save registers
mov ax, word ptr [bx].lpinque ; get chars in spool
mov dx, word ptr [bx].lpinque+2 ; dx:ax = in spool
mov cx, 100 ; cx = multiplier
call mmul ; dx:ax *= 100
push ax ; save product
push dx
mov ax, nextqrnbr ; ax = nbr of recs in queue
xor dx, dx ; dx:ax = nbr of recs
mov cx, qrdlen ; cx = size of qrecord
mul cx ; dx:ax = que rec space avail
add ax, lpt_dqsz ; add in memory puddle size
adc dx, 0 ; dx:ax = bytes available
mov cx, dx ; setup for 32bit division
mov bx, ax ; cx:bx = divisor
pop dx ; get dividend
pop ax
call ddiv ; divide to get percent used
cmp ax, 100 ; q. greater than 100%
jna dgauge10 ; a. no .. continue
mov ax, 100 ; ax = max of 100%
dgauge10: push ax ; save for later
mov di, si ; di -> output string
add di, 4 ; di -> output field
call dformat ; put in percentage
pop ax ; restore register
push es ; save register
push ds ; setup addressibility
pop es ; ..to our string
mov di, si ; di -> output string
add di, 7 ; di -> output field
xor dx, dx ; dx = 0 for the divide
mov bx, 10 ; bl = divisor for gauge
div bx ; ax = quotient, dx = rmdr
push ax ; save nbr of whole blks
xor cx, cx ; assume no chars prtd
or ax, dx ; q. any chars needed?
pop ax ; .. restore count
jz dgauge40 ; a. no .. blank it.
push ax ; .. save again
or ax, ax ; q. any whole blks?
jz dgauge30 ; a. no .. try parts
mov cx, ax ; cx = loop count
mov al, 219 ; al = whole block char
dgauge20: cld ; assure direction fwd
rep stosb ; put in whole blocks
dgauge30: pop cx ; restore whole count
or dx, dx ; q. any partials?
jz dgauge40 ; a. no .. blank rest
cmp dx, 5 ; q. partial?
jbe dgauge35 ; a. yes .. setup for one
mov byte ptr [di], 219 ; else .. another whole
jmp short dgauge37 ; put out char
dgauge35: mov byte ptr [di], 221 ; put out partial char
dgauge37: inc di ; di -> next output location
inc cx ; ax = output'd length
dgauge40: neg cx ; cx = minus max length
add cx, 10 ; cx = nbr blanks
mov al, '-' ; al = blanking character
repnz stosb ; blank remainder of line
pop es ; restore registers
pop bx
ret ; ..and return to caller
dgauge endp
; ----------------------------------------------------------------------
; This routine does a 32bit unsigned division.
;
; Entry
; dx:ax = dividend
; cx:bx = divisor
;
; Exit
; dx:ax = quotient
; ----------------------------------------------------------------------
ddiv proc
push cx ; make a work area [bp + 8]
push bx ; from the stack [bp + 6]
push dx ; [bp + 4]
push ax ; [bp + 2]
push bp ; save registers
mov bp,sp ; setup arg pointer
push si ;
or bx, cx ; q. dividing by zero?
jnz ddiv2 ; a. no .. continue
xor ax, ax ; else .. return a zero
xor dx, dx ; ...
jmp short ddiv7 ; return to caller
ddiv2: mov ax, cx ; ax = divisor msb
or ax, ax ; q. 32bit / 16bit?
jnz ddiv3 ; a. no .. 32bit / 32bit
; 32bit / 16bit divide
mov cx, [bp + 6] ; cx = divisor lsb
mov ax, [bp + 4] ; ax = dividend msb
xor dx, dx ; dx = 0
div cx ; dx:ax = msb / lsb
mov bx, ax ; bx = temp result
mov ax, [bp + 2] ; ax = dividend lsb
div cx ; dx:ax = temp result
mov dx, bx ; dx = temp msb result
jmp ddiv7 ; ..return to call thru exit
; 32bit / 32bit divide
ddiv3: mov bx, ax ; bx = divisor msb
mov cx, [bp + 6] ; cx = divisor lsb
mov dx, [bp + 4] ; dx = dividend msb
mov ax, [bp + 2] ; ax = dividend lsb
ddiv4: shr bx, 1 ; shift reduction
rcr cx, 1 ; of both 32bit operands
shr dx, 1 ; . . .
rcr ax, 1 ; . . .
or bx, bx ; q. done reducing?
jnz ddiv4 ; a. no .. loop back
div cx ; dx:ax = 1st result
mov si, ax ; si = save temp
mul word ptr [bp + 8] ;
xchg cx, ax ; swap temp values
mov ax, [bp + 6] ; ax = divisor lsb
mul si ;
add dx, cx ;
jb ddiv5 ;
cmp dx, [bp + 4] ;
ja ddiv5 ;
jb ddiv6 ;
cmp ax, [bp + 2] ; q. result < original lsb?
jbe ddiv6 ; a. yes .. exit here
ddiv5: dec si ; di =
ddiv6: xor dx, dx ; dx = 0
mov ax, si ; ax = result
ddiv7: pop si ; restore registers
pop bp ;
add sp, 8 ; adjust stack for work area
ret ; ..and return to caller
ddiv endp
; ----------------------------------------------------------------------
; This routine does a 32bit by 16bit multiply
;
; Entry
; dx:ax = multiplier
; cx = multiplicand
;
; Exit
; dx:ax = product
; ----------------------------------------------------------------------
mmul proc
push si ; save regs
push di
mov di, dx ; save upper value
mul cx ; .. multiply lower
push ax ; .. save lower product
mov si, dx ; .. save upper product
mov ax, di ; ax = upper value
mul cx ; multiply upper
mov dx, ax ; dx = partial product
add dx, si ; dx = full upper product
pop ax ; dx:ax = 32 bit product
pop di ; restore regs
pop si
ret
mmul endp
; ----------------------------------------------------------------------
;
; qh - queue handler, device independant queue interface routine
;
; Read a queue record
;
; This function is used to get a buffer off of a printer's queue.
; The printer queues are fifo queues.
;
; On return, the carry flag indicates one of the following:
; - There are no more records on the specified printer queue
; - An invalid queue number was provided.
;
; When an expandable buffer (disk, EMS) is used and freepool is empty,
; one or more new records will be allocated at the end of the queue.
; Reading a record automatically puts it on the freepool queue.
;
; Entry: ah = 0
; al = queue to read (0,1,2 = lpt1,2,3)
; ds:dx -> buffer
;
; Exit: bx = queue record # read
; carry = error
; al = exit code, 00 = success
; 01 = invalid queue number
; 02 = no data on queue
; 03 = unable to read at this time.
;
;
; Write a queue record
;
; This function is used to add a queue record to a printer queue. This
; function is performed by:
; - get a free record from queue (allocate if needed)
; - writing the new record
; - updating the chains
;
; On return, the carry flag indicates success or failure.
; The function may fail for one of the following reasons:
; - no more space in queue
; - unable to write queue record (DOS in use)
; - invalid queue number
;
; Entry: ah = 1
; al = queue to write (0,1,2 = lpt1,2,3)
; ds:dx -> buffer
;
; Exit: carry = error
; al = exit code, 00 = success
; 01 = invalid queue number
; 02 = space exhausted
; 03 = unable to write at this time.
;
; ----------------------------------------------------------------------
qh proc
push bx ; save registers
push cx
push dx
inc qinuseflg ; show qh in use
cmp al, 2 ; q. valid queue number?
jna qh00 ; a. yes .. continue
mov al, 1 ; al = invalid queue error
jmp short qh92 ; .. return w/error
qh00: or ah, ah ; q. read?
jnz qh01 ; a. no .. skip the call
call qhr ; else .. read a record
jmp short qh95 ; .. and return to caller
qh01: dec ah ; q. write?
jnz qh90 ; a. no .. bad function
call qhw ; else .. write a record
jmp short qh95 ; .. and return to caller
qh90: mov al, 0ffh ; al = bad function request
qh92: stc ; .. carry on
qh95: dec qinuseflg ; let someone else use rtn
pop dx ; restore registers
pop cx
pop bx
ret ; return to caller
qh endp ; end of queue handler main
; ---------------------
; read a queue record
; ---------------------
qhr proc
mov bl, lplen ; bl = length of lpstruc
mul bl ; ax = offset of printer CB
mov bx, ax ; bx => requested queue
add bx, offset qhlpt1 ; bx -> requested queue
cmp [bx].lpfirstqr, 0 ; q. records on queue?
jne qhr05 ; a. yes .. get first record
mov al, 2 ; al = no data on queue
jmp short qhr90 ; .. return w/error
qhr05: mov cx, [bx].lpfirstqr ; cx = next queue record
mov ax, 1 ; ax = read queue record
call dword ptr qrtn ; q. get go ok?
jnc qhr10 ; a. yes .. update queues
mov al, 3 ; al = can't read now
jmp short qhr90 ; .. return w/error
qhr10: mov si, dx ; si -> record just read
mov ax, [si].qrnextqr ; ax = nxt rec nbr on lp que
mov [bx].lpfirstqr, ax ; remove record from lp que
or ax, ax ; q. end of queue?
jnz qhr20 ; a. no .. add to freepool
mov [bx].lplastqr, ax ; else .. free last lp rec
qhr20: mov ax, qhfirstf ; ax -> first free record
mov qhfirstf, cx ; first free = record read
mov [si].qrnextqr, ax ; queue record = new next
mov ax, 4 ; ax = rewrite ptr as free
call dword ptr qrtn ; .. write to queue
xor al, al ; al = all is well
clc ; .. no carry
ret ; return to caller
qhr90: stc ; show error
ret ; .. return to caller
qhr endp
; ---------------------
; write a queue record
; ---------------------
qhw proc ; write a queue record
push dx ; save buffer pointer
mov bl, lplen ; bl = length of lpstruc
mul bl ; ax = offset of printer CB
mov bx, ax ; bx -> requested queue
add bx, offset qhlpt1 ; bx -> requested queue
mov cx, qhfirstf ; q. any free records
jcxz qhw10 ; a. no .. allocate new one
mov dx, offset qhfirstf ; dx -> new first free
mov ax, 3 ; ax = read ptr of 1st free
call dword ptr qrtn ; q. read pointer ok?
jnc qhw20 ; a. yes .. continue
mov al, 3 ; al = can't right now.
mov qhfirstf, cx ; put record on freepool
pop dx ; restore buffer pointer
jmp short qhw90 ; ..then return w/error
qhw10: mov ax, 5 ; ax = allocate new record
call dword ptr qrtn ; q. new record available?
jnc qhw20 ; a. yes .. continue
mov al, 3 ; al = can't write now
pop dx ; .. restore buffer pointer
jmp short qhw90 ; .. return w/error
qhw20: pop si ; si -> record, cx = rec nbr
mov dx, si ; dx -> our record
mov [si].qrnextqr, 0 ; .. set last record pointer
mov ax, 2 ; ax = write our record
call dword ptr qrtn ; q. write ok?
jnc qhw25 ; a. yes .. continue
mov al, 3 ; al = can't write now.
jmp short qhw90 ; .. return w/error
qhw25: cmp [bx].lpfirstqr, 0 ; q. queue empty?
je qhw30 ; a. yes .. make 1st & last
xchg cx, [bx].lplastqr ; cx <-> last on queue
mov ax, 4 ; ax = write pointer only
lea dx, [bx].lplastqr ; dx -> new last record nbr
call dword ptr qrtn ; .. update prev last record
jmp short qhw80 ; .. return ok
qhw30: mov [bx].lpfirstqr, cx ; set as first record
mov [bx].lplastqr, cx ; .. and last record
qhw80: xor al, al ; al = no problem
clc ; .. no carry
ret ; .. return to caller
qhw90: stc ; carry .. problem
ret ; .. return to caller
qhw endp
; ---------------------------------------------------------------------
; Swap display with buffer memory;
;
; Returns: carry if bad video mode mode
; ---------------------------------------------------------------------
swapdisp proc
push bp ; save registers
mov ah, 0fh ; get current display mode
int 10h ; .. ask BIOS
cmp al, 2 ; q. BW mode?
je swpdsp10 ; a. yes .. ok to do it.
cmp al, 3 ; q. Color mode?
je swpdsp10 ; a. yes .. ok to do it.
cmp al, 7 ; q. Mono mode?
je swpdsp10 ; a. yes .. ok to do it.
stc ; show error
jmp short swpdsp90 ; .. return now
swpdsp10: mov si, offset scrbuf ; si -> screen buffer
mov di, lines ; di = rows to swap
xor dh, dh ; dh = start on line 0
swpdsp15: mov cx, cols ; columns to swap
xor dl, dl ; dl = start at pos 0
swpdsp20: mov ah, 2 ; ah = set cursor position
int 10h ; .. set the cursor
mov ah, 8 ; ah = read char & attr
int 10h ; .. well.. do it
xchg ax, [si] ; swap with buffer
inc si ; si -> next
inc si ; .. char
push cx ; save counter
mov bl, ah ; bl = attr to write
mov ah, 9 ; ah = write char @ cursor
mov cx, 1 ; cx = # to write
int 10h ; .. write char
pop cx ; .. restore counter
inc dl ; dl = next column
loop swpdsp20 ; .. loop 'til all line done
inc dh ; dh = next line
dec di ; q. all lines done?
jnz swpdsp15 ; a. no .. next line
clc ; clear error flag
swpdsp90: pop bp ; restore registers
ret ; return to caller
swapdisp endp
; ----------------------------------------------------------------------
; Start routine; startup processing for spool
;
; This routine performs the following functions:
; - Call CMD to perform initial processing
; - Move the qrtn to qhandler
; - Set the final qrtn address
; - Calculate paragraphs to keep (including CONV MEM if needed)
; - Set all interrupt vectors
; - Go TSR
; ----------------------------------------------------------------------
start proc
push ds ; save entry ds
call cmd ; initialize the system
rep movsb ; .. move the qhandler rtn
pop ds ; restore ds
mov qrtnseg, dx ; setup new qhandler
mov dx, nxtavail ; dx = bytes used
add dx, 15 ; .. next para if needed
mov cl, 4 ; cl = shift count
shr dx, cl ; dx = paragraph count
test pcsflg, pcsflgcm ; q. conventional spool?
jz start50 ; a. no addtl allocation
mov ax, cs ; ax = start paragraph
add ax, dx ; ax = para of mem queue
mov memqseg, ax ; save segment of conv area
mov ax, memqsize ; ax = conv mem size
mov bl, (qrlen/16) ; bl = paragraphs/qrecord
mul bl ; ax = paragraphs for queue
add dx, ax ; dx = paragraphs to keep
start50: push dx ; save memory to keep
mov cx, clrlen ; cx = length to clear
mov di, startclr ; di -> where to clear
mov al, 0 ; al = initial memory value
rep stosb ; clear allocated areas
mov si, intblk1 ; si -> 1st interrupt block
mov cx, intblkcnt ; cx = nbr of ints handled
start60: mov ah, 35h ; ah = get interrupt vector
mov al, [si+12] ; al = interrupt number
int 21h ; .. get the current setting
mov [si+2], es ; save segment of old int
mov [si], bx ; .. and offset
mov dx, [si+13] ; dx -> new interrupt
mov ah, 25h ; ah = set interrupt vector
int 21h ; .. set up new vector
add si, intblklen ; si -> next entry
loop start60 ; .. set next interrupt
dec i21hflg ; open flood gate for int 21
pop dx ; restore memory to keep
mov ax, 3100h ; ax = TSR, rc = 0
int 21h ; .. hope & pray
start endp
align 16 ; align to a paragraph
; ---------------------------------------------------------------------
; Screen buffer
; ---------------------------------------------------------------------
scrbuf db '║ LPT Gauge CPS CP CIQ '
db ' Time │ D: Disable P: Pause G:Go ║'
db '║ 1: '
db ' │ F: Formfeed R: Reset Esc ║'
db '║ 2: '
db ' │ J: JobSkip C: Cancel ║'
db '║ 3: '
db ' │ CTRL+key:LPT2, ALT+key:LPT3 ║'
db '╚═══════════════════════════════════════'
db '═════════╧═════════════════════════════╝'
scrbufcon equ $-1
; ----------------------------------------------------------------------
; Note: Everything beyond this is overlaid after initialization.
; ----------------------------------------------------------------------
scrbufend equ scrbufcon + 400
qhandler equ scrbuf + 800
; ----------------------------------------------------------------------
; The queue handler will be moved to this address after initialization
; ----------------------------------------------------------------------
; ----------------------------------------------------------------------
; Initialization code data areas -- available during init only
; ----------------------------------------------------------------------
hdrmsg db "PCSPOOL V1.0 (c) 1990, Ziff Communications Co.",13,10
db "PC Magazine ",254," Michael Holmes and Bob Flanders"
db 13,10
dollar db "$"
help db 10
db "Usage:",13,10,10
db " PCSPOOL /I [/1] [/2] [/3] [/Cnn|/D[d:\path\]]",13,10
db " PCSPOOL /P [/1|/2|/3] [Comment ..]",13,10
db " PCSPOOL /F [/1|/2|/3]",13,10
db " PCSPOOL /U",13,10,"$"
invreq db 10
db "Invalid request - "
invreqcd db " ",13,10,"$"
invopnd db 10
db "Invalid operand - "
invopndcd db " ",13,10,"$"
cantinitq db 10
db "Unable to initialize queue.",13
notup db 10
db "Spooler not installed.",13,10,"$"
upalrdy db 10
db "Spooler already installed.",13,10,"$"
notspld db 10
db "Requested printer not spooled.",13,10,"$"
nodesq db 10
db "This utility does not work with multitasking."
db 13,10,"$"
splalrdy db 10
db "You may only specify 1 spool type.",13,10,"$"
invmemz db 10
db "Invalid memory size specified. "
db "Must be 1 to 64.",13,10,"$"
baddrive db 10
db "Invalid drive specified.",13,10,"$"
cantfree db 10
db "Can't uninstall at this time.",13,10,"$"
freeok db 10
db "Uninstalled successfully.",13,10,"$"
pauseok db 10
db "Pause issued successfully.",13,10,"$"
waitmsg db 10
db "Waiting for print to complete. ESC cancels shutdown."
db 13,10
db "Hit any other key to uninstall immediately.",13,10,"$"
uninscan db 10
db "Uninstall cancelled. PCSPOOL still resident."
db 13,10,"$"
ffok db 10
db "Formfeed sent to printer.",13,10,"$"
qfilenm db "PCSPOOL.QUE",0 ; Q's filename
char_C db "C" ; default to conventional
; ----------------------------------------------------------------------
;
; Initialization procedure; cs, ds, es -> our segment
;
; Return: ds:si -> queue routine
; es:di -> new address
; cx = len to move
; dx = segment of new addr
;
; 1. Scan for a commmand
; - if error, issue error message & exit
;
; 2. Call appropriate command processor
;
; 3. Setup regs for move of queue handler routine
;
; ----------------------------------------------------------------------
cmd proc ; find/process command
push es ; save regs
mov dx, offset hdrmsg ; ds:dx -> header message
mov ah, 9 ; ah = print ASCII$ string
int 21h ; .. display header
mov cx, 'DE' ; cx = DE of DESQ
mov dx, 'SQ' ; dx = SQ of DESQ
mov ax, 2b01h ; ax = set invalid date
int 21h ; .. let DOS have at it
cmp al, 0ffh ; q. desqview active?
je cmd0 ; a. no .. continue
mov dx, offset nodesq ; dx -> not desqview compatible
xor al, al ; al = no help needed
call die ; .. die .. with honor
cmd0: mov ah, 0c4h ; ah = query spooler active
int 17h ; q. spooler running?
mov bx, 81h ; bx -> command line
call nxtop ; q. any operands?
jnc cmd00 ; a. yes .. process
mov dx, offset dollar ; dx -> null message
mov al, 1 ; al = give help
call die ; terminal w/help
cmd00: cmp al, 'I' ; q. Init request?
jnz cmd10 ; a. no .. check next
call init ; else.. call init code
jmp short cmd90 ; return to caller
cmd10: cmp al, 'P' ; q. pause request?
jnz cmd20 ; a. no .. check next
jmp pause ; else.. goto pause rtn
cmd20: cmp al, 'U' ; q. uninstall?
jnz cmd30 ; a. no .. check next
jmp uninstall ; else.. call uninstall
cmd30: cmp al, 'F' ; q. formfeed?
jnz cmd40 ; a. no .. error
jmp formfeed ; else.. send an ff
cmd40: mov invreqcd, al ; save error code
mov dx, offset invreq ; ds:dx -> error message
mov al, 1 ; al = give help
call die ; Message & die
cmd90: pop es ; restore regs
lea di, qhandler ; di -> qhandler rtn
mov dx, di ; dx = offset also
mov cl, 4 ; cl = shift amount
shr dx, cl ; dx = segment offset
mov ax, cs ; ax = program segment
add dx, ax ; dx = qhandler segment
xor si, si ; si = 0 offset
mov cx, qrtnlen ; cx = size of routine
cld ; direction = up
mov ds, qrtnseg ; ds = current seg of rtn
ret ; return to caller
cmd endp ; end of scan routine
; ----------------------------------------------------------------------
; nxtop: find next operand; bx -> current position
;
; Returns: bx -> 1st char (after slash) of next op
; al = 1st letter of opnd (capitalized)
; carry = end of line
; ----------------------------------------------------------------------
nxtop proc
cmp byte ptr [bx], 0dh ; q. end of line?
je nxtop80 ; a. yes .. scan no more
cmp byte ptr [bx], '/' ; q. slash?
je nxtop50 ; a. yes .. return next char
cmp byte ptr [bx], ' ' ; q. whitespace?
jna nxtop20 ; a. yes .. skip whitespace
; skip non-white space
nxtop10: inc bx ; bx -> next char
cmp byte ptr [bx], 0dh ; q. end of line?
je nxtop80 ; a. yes .. scan no more
cmp byte ptr [bx], '/' ; q. slash?
je nxtop50 ; a. yes .. return next char
cmp byte ptr [bx], ' ' ; q. whitespace?
ja nxtop10 ; a. no .. skip it
nxtop20: inc bx ; bx -> next char
cmp byte ptr [bx], 0dh ; q. end of line?
je nxtop80 ; a. yes .. scan no more
cmp byte ptr [bx], '/' ; q. slash?
je nxtop50 ; a. yes .. return next char
cmp byte ptr [bx], ' ' ; q. whitespace?
jna nxtop20 ; a. yes .. get next char
jmp short nxtop60 ; else .. return char
nxtop50: inc bx ; bx -> byte after slash
nxtop60: mov al, [bx] ; al = byte of operand
cmp al, 'a' ; q. char >= lower case a?
jna nxtop90 ; a. no .. return as is
cmp al, 'z' ; q. char <= lower case z?
jnb nxtop90 ; a. no .. return as is
and al, not 20h ; xlate to upper case
jmp short nxtop90 ; return to caller, ok
nxtop80: stc ; carry = end of line
ret ; return to caller
nxtop90: clc ; no carry = al contains char
ret ; return to caller
nxtop endp
; ----------------------------------------------------------------
; Process the init command; bx -> next char to process
; di = B0BFh if spooler is active
; ----------------------------------------------------------------
init proc
cmp di, 0B0BFh ; q. spooler already active?
jne init05 ; a. no .. ok to INIT.
mov dx, offset upalrdy ; dx -> up already message
xor al, al ; al = no help needed
call die ; .. die .. with honor
init05: push es ; save es
mov es, ds:[002ch] ; es -> environment
mov ah, 49h ; ax = release it
int 21h ; .. tell dos to make it so
push bx ; save register
mov ah, 34h ; ah = get DOS busy flg addr
int 21h ; call DOS
mov word ptr dos_busy, bx ; save offset
mov word ptr dos_busy+2, es ; ..and segment of busy flag
pop bx ; restore registers
pop es ;
init10: call nxtop ; q. any more options?
jc init50 ; a. no .. process options
cmp al, 'C' ; q. conventional?
jne init15 ; a. no .. try next
call getmemz ; get memory size
jmp init10 ; .. get next operand
init15: cmp al, 'D' ; q. disk?
jne init20 ; a. no .. try printer
call getfile ; get spooler file
jmp init10 ; .. get next operand
init20: mov cl, al ; prepare to test for printer
sub cl, '1' ; .. set to 0, 1 or 2 ..
cmp cl, 2 ; q. lpt1, 2 or 3?
ja init30 ; a. no .. error
mov al, 1 ; al = bit
shl al, cl ; .. shift to proper position
or pcsflg, al ; .. turn on bit for printer
jmp init10 ; .. get the next operand
init30: mov invopndcd, al ; save invalid operand
mov dx, offset invopnd ; dx -> error message
mov al, 1 ; .. give 'em help
call die ; tell the operator, then die
init50: test pcsflg, pcsflgsx ; q. any spool type set?
jnz init60 ; a. yes .. setup mem, etc
or pcsflg, pcsflgcm ; else .. setup for conventional
lea bx, char_C ; bx -> 'C' for conv spool
mov ax, memqsize ; ax = k bytes for queue
shl ax, 1 ; ax = nbr of que records
mov memqsize, ax ; set up max record cnt
call initq ; initialize queue rtn
init60: call setmem ; setup mem, buffer ptr's, etc
call scrinit ; init the screen buffer
ret ; return to caller
init endp
; ----------------------------------------------------------------
; Process the pause command; bx -> next char to process
; di = bobfh if spooler is active
; ----------------------------------------------------------------
pause proc
cmp di, 0B0BFh ; q. spooler active?
je pause05 ; a. yes.. continue
mov dx, offset notup ; dx -> not up message
call die
pause05: xor dx, dx ; assume printer 0
call nxtop ; find the next operator
jc pause10 ; if end of line..
cmp byte ptr [bx]-1, '/' ; q. switch?
jne pause10 ; a. no .. must be comment
sub al, '1' ; al = adjust as printer nbr
cmp al, 2 ; q. printer number?
ja pause10 ; a. no.. part of comment
mov dl, al ; dx = new printer number
call nxtop ; bx -> next operator
pause10: mov si, bx ; si -> comment, if any
xor cx, cx ; cx = char counter
pause15: cmp byte ptr [bx], 0dh ; q. end of line?
je pause18 ; a. yes .. make it asciiz
inc bx ; bx -> next char
inc cx ; cx = char counter
jmp pause15 ; .. look at next char
pause18: mov byte ptr [bx], 0 ; ASCIIZ the string
mov ah, 0c0h ; ah = get control block
int 17h ; es:bx = control block
cmp es:[bx].lpstatus, 0feh ; q. spooled?
jb pause20 ; a. yes.. process pause
mov dx, offset notspld ; dx -> not spooled message
mov al, 1 ; al = give help
call die ; die gracefully
pause20: mov ah, 0c1h ; ah = build control record
int 17h ; .. ask spooler to do it
mov dx, offset pauseok ; ds:dx -> completion msg
xor al, al ; al = no help
call die ; Message & die
pause endp
; ----------------------------------------------------------------
; Process the formfeed command; bx -> next char to process
; ----------------------------------------------------------------
formfeed proc
xor dx, dx ; assume printer 0
call nxtop ; find the next operator
jc formfd10 ; if end of line.. send ff
sub al, '1' ; al = adjust as printer nbr
cmp al, 2 ; q. printer number?
ja formfd10 ; a. no.. ignore it
mov dl, al ; dx = printer number
formfd10: mov ax, 000ch ; ax = write a formfeed
int 17h ; .. ask spooler to do it
cmp ah, 1 ; q. print ok?
je formfd10 ; a. no .. try again
mov dx, offset ffok ; ds:dx -> completion msg
xor al, al ; al = no help
call die ; Message & die
formfeed endp
; ----------------------------------------------------------------------
; Process the uninstall command; bx -> next char to process
; di = b0bfh if spooler is active
; si = segment of active spooler
; ----------------------------------------------------------------------
uninstall proc
cmp di, 0b0bfh ; q. spooler active?
je unins05 ; a. yes .. continue
mov dx, offset notup ; dx -> message
jmp die ; .. die now, sucker
unins05: call needwait ; q. need to wait?
jnc unins09 ; a. no .. continue
lea dx, waitmsg ; dx -> wait message
mov ah, 9 ; ah = print ASCII$ message
int 21h ; print message
unins08: call needwait ; q. need to wait more?
jnc unins09 ; a. no .. continue
mov ah, 0bh ; ah = check keyboard status
int 21h ; call DOS
cmp al, 0ffh ; q. character waiting?
jne unins08 ; a. no .. continue waiting
mov ah, 08h ; ah = get char
int 21h ; al = char
cmp al, 1bh ; q. escape?
jne unins09 ; a. no .. continu
mov dx, offset uninscan ; dx -> cancelled message
jmp short unins90 ; .. and finish now
unins09: mov al, 0c7h ; ah = close queue request
int 17h ; call resident PCSPOOL
mov ds, si ; ds -> spooler's memory
mov es, si ; es -> same
mov di, intblk1 ; bx -> first interrupt blk
mov cx, intblkcnt ; cx = number of ints used
unins10: mov ah, 35h ; ah = get interrupt
mov al, [di+12] ; al = interrupt number
int 21h ; es:bx -> interrupt
mov ax, es ; ax = int segment
cmp ax, si ; q. our segment?
jne unins80 ; a. no .. can't uninstall
add di, intblklen ; si -> next block
loop unins10 ; .. check next block
mov di, intblk1 ; si -> first interrupt blk
mov cx, intblkcnt ; cx = number of ints used
cli ; no ints ..
unins20: lds dx, dword ptr es:[di] ; ds:dx -> old interrupt rtn
mov ah, 25h ; ah = set interrupt
mov al, es:[di+12] ; al = int to set
int 21h ; .. set the interrupt
add di, intblklen ; si -> next block
loop unins20 ; .. reset next interrupt
sti ; .. allow interrupts again
mov ah, 49h ; ah = free (spooler's) mem
int 21h ; .. do it
mov dx, offset freeok ; dx -> freed ok.
jmp short unins90 ; .. end the job, painlessly
unins80: mov dx, offset cantfree ; dx -> can't free message
unins90: xor al, al ; al = no help
push cs ; restore our ..
pop ds ; ..own segment
call die ; die .. hard & fast
uninstall endp
; ----------------------------------------------------------------------
; This routine checks for the need to wait for the spooler
; to queuese(?)
;
; Returns
; carry = wait needed
;
; ----------------------------------------------------------------------
needwait proc
push cx ; save registers
push dx
mov cx, 3 ; cx = loop counter
xor dx, dx ; dx = lpt1:
needwa10: mov ax, 0c600h ; ah = check wait condition
int 17h ; call printer handler
or al, al ; q. need to wait?
jz needwa80 ; a. no .. check next one
needwa20: stc ; wait needed
jmp short needwa90 ; ..and exit w/carry set
needwa80: inc dx ; dx = next printer
loop needwa10 ; .. loop again
clc ; no wait needed
needwa90: pop dx ; restore registers
pop cx
ret ; return to caller
needwait endp
; ----------------------------------------------------------------------
; Get memory spool size req; bx -> "C" of operand
;
; return: queuez is set to size
; pcsflgcm is set on
; ----------------------------------------------------------------------
getmemz proc
test pcsflg, pcsflgsx ; q. any spool type set?
jz getmemz10 ; a. no .. setup memory size
mov dx, offset splalrdy ; dx -> spooled type sel'd
mov al, 1 ; al = give 'em help
call die ; play taps
getmemz10: or pcsflg, pcsflgcm ; show conventional chosen
mov ax, memqsize ; ax = default queue size
cmp byte ptr [bx+1], ' ' ; q. whitespace?
jna getmemz80 ; a. yes .. use default
cmp byte ptr [bx+1], '/' ; q. slash?
je getmemz80 ; a. yes .. use default
xor ax, ax ; clear ax for value calc
mov cx, [bx+1] ; cx = spool size chars
sub cx, '00' ; convert to binary
cmp cl, 9 ; q. first char value valid?
ja getmemz70 ; a. no .. error
mov al, cl ; al = first value
cmp ch, 9 ; q. 2nd digit valid?
ja getmemz80 ; a. no .. continue
mov cl, 10 ; ch = multiplier
mul cl ; ax = ax * 10
mov cl, ch ; setup cl w/2nd digit
xor ch, ch ; ch = 0
add ax, cx ; ax = total memory size
cmp ax, 64 ; q. valid value?
ja getmemz70 ; a. no .. error
cmp ax, 4 ; q. lower bounds ok?
jnb getmemz80 ; a. no .. error
getmemz70: mov dx, offset invmemz ; dx -> invalid memory size
mov al, 1 ; al = give 'em help
call die ; .. ice 'im
getmemz80: shl ax, 1 ; ax = nbr of que records
mov memqsize, ax ; set up max record cnt
getmemz90: call initq ; return to caller
ret
getmemz endp
; ----------------------------------------------------------------
; Get the spool file name; bx -> "D" of operand
; ----------------------------------------------------------------
getfile proc
test pcsflg, pcsflgsx ; q. any spool type set?
jz getfile10 ; a. no .. setup filename
mov dx, offset splalrdy ; dx -> spooled type sel'd
mov al, 1 ; al = help the helpless
call die ; play taps
getfile10: or pcsflg, pcsflgdk ; show disk spooling chosen
cmp byte ptr [bx+1], ' ' ; q. whitespace?
jna getfile50 ; a. yes .. use default
cmp byte ptr [bx+1], '/' ; q. slash?
je getfile50 ; a. yes .. use default
lea si, [bx+1] ; si -> next char in cmd
mov di, offset qfile ; di -> filename work area
cld ; .. move & scan up
getfile20: movsb ; move a byte to work area
cmp byte ptr [si], ' ' ; q. whitespace?
jna getfile40 ; a. yes .. end of parm
cmp byte ptr [si], '/' ; q. switch?
jne getfile20 ; a. yes .. end of parm
getfile40: cmp byte ptr [di-1], ':' ; q. last char a colon?
je getfile45 ; a. yes.. no path wanted
cmp byte ptr [di-1], '\' ; q. last char backslash?
je getfile45 ; a. yes.. path ok.
mov byte ptr [di], '\' ; end path w/backslash
inc di ; di -> next char
getfile45: mov byte ptr [di], 0 ; assure nul after path
getfile50: mov di, offset qfile ; di -> filename work area
mov cx, 65 ; cx = length to scan
xor al, al ; .. for a nul
repnz scasb
dec di ; di -> first nul
mov si, offset qfilenm ; si -> queue file name
getfile55: movsb ; move a char
cmp byte ptr [si-1], 0 ; q. end of move?
jne getfile55 ; a. no .. continue move
getfile90: call initq ; initialize the queue rtn
ret ; return to caller
getfile endp
; ----------------------------------------------------------------
; setup all memory addresses; qrtnlen = length of queue rtn
;
; Return: Memory pointers setup:
; - live prt memory puddles
; - live prt queue rec buffers
; - interrupt stacks
; - single dequeue record
;
; Note: The conventional memory buffer cannot be setup here because
; this mem must first be freed before the buffer is allocated.
; The qrtn pointer is not set up in this routine. It is set up
; when the routine is moved to the final qrtn area.
;
; A "live prt" is a printer whose address is noted in the
; printer vector at absolute address 40:8.
; ----------------------------------------------------------------
setmem proc
push ax ; save caller regs
push bx
push cx
push dx
push si
push di
push es
mov si, nxtavail ; si -> next available byte
add si, qrtnlen ; si -> byte after handler
mov startclr, si ; save start of allocateds
mov bx, offset qhlpt1 ; bx -> first printer
mov cx, 3 ; max printers
xor ax, ax ; ax = 0
mov es, ax ; es -> low memory
setmem10: mov di, [bx].lpprtnbr ; dx = printer number
shl di, 1 ; dx = dx * 2
mov dx, word ptr es:[di]+408h ; dx = base prt addr
or dx, dx ; q. any more printers?
jz setmem15 ; a. no .. exit
cmp dx, 100h ; q. local printer
jb setmem13 ; a. no .. next prt
inc dx ; dx = status port
mov [bx].lpstatport, dx ; save for later
mov dx, si ; dx = next available byte
push cx ; save cx
mov cl, 4 ; cl = shift amt
shr dx, cl ; dx = dx / 16
pop cx ; .. restore cx
mov ax, ds ; ax = pgm segment
add ax, dx ; ax = puddle seg
mov word ptr [bx].lpdqbuf+2, ax ; save puddle segment
add si, lpt_dqsz ; si -> next byte
mov [bx].lpwrkqr, si ; save queue record address
push cx ; save register
mov cx, [bx].lpprtnbr ; cx = printer nbr
mov al, 1 ; al = a bit
shl al, cl ; al = bit mask
pop cx ; restore cx
test pcsflg, al ; q. spool this printer?
jz setmem11 ; a. no .. mark not spooled
mov [bx].lpstatus, 0 ; setup status as spooled
jmp short setmem12 ; ..and continue
setmem11: mov [bx].lpstatus, 0ffh ; status is not spooled
setmem12: add si, qrlen ; si -> next available byte
setmem13: add bx, lplen ; bx -> next lp routine
loop setmem10 ; set up next printer
setmem15: mov bx, intblk1 + 8 ; bx -> first new ss
mov cx, intblkcnt ; cx -> number of new ss
setmem20: mov [bx], si ; save pointer to stack
mov [bx]+2, ds ; .. and segment
add si, intstksz ; si -> next stack area
add bx, intblklen ; bx -> next pointer
loop setmem20 ; .. build next stack
mov dqrec, si ; si -> dequeue record
add si, qrlen ; si -> next available byte
mov nxtavail, si ; save new next avail ptr
sub si, startclr ; si = length to clear
mov clrlen, si ; save for later
pop es ; restore caller regs
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
setmem endp
rsetints proc
rsetints endp
; ----------------------------------------------------------------------
; Init the queue handler;
; ----------------------------------------------------------------------
initq proc
call findrtn ; find the queue routine
mov word ptr qrtn, 3 ; setup the offset for the call
xor ax, ax ; ax = 0 = init spool
call dword ptr qrtn ; call the handler
jnc initq90 ; .. return if all ok
mov dx, offset cantinitq ; dx -> init q error
xor al, al ; al = no help
call die ; ..die in flames
initq90: ret
initq endp
; ----------------------------------------------------------------------
; Find the queue handler; bx -> first char of switch
;
; Return: qrtnseg = segment of routine
; qrtnlen = length of routine (in bytes)
; ----------------------------------------------------------------------
findrtn proc ; find abs address of rtn
mov al, [bx] ; al = first char of switch
and al, NOT 20h ; upper case it.
mov si, offset mainlen ; si -> start of 1st qrtn
findrtn10: cmp [si+2], al ; q. this the routine?
je findrtn50 ; a. yes .. process the addr
add si, [si] ; si -> next routine
jmp findrtn10 ; .. find the next routine
findrtn50: mov dx, [si] ; dx = length of routine
mov qrtnlen, dx ; .. save the routine length
mov cl, 4 ; cl = shift amt
shr si, cl ; .. div by 16
mov ax, cs ; ax = current segment
add ax, si ; ax = driver's segment
mov qrtnseg, ax ; save segment of routine
ret ; return to caller
findrtn endp
; ----------------------------------------------------------------------
; initiailize screen stuff;
; ----------------------------------------------------------------------
scrinit proc ; init the screen buffer
push ax ; save registers
push bx
push cx
push dx
push si
push di
mov ah, 0fh ; get current display mode
int 10h ; .. ask BIOS
cmp al, 3 ; q. Color mode?
jne scrinit10 ; a. No .. leave in mono
mov ourattr, colattr ; else .. assume color attr
scrinit10: mov di, offset scrbufend ; di -> end of buffer
mov si, offset scrbufcon ; si -> end of constants
mov al, ourattr ; al = attribute to use
mov cx, 400 ; .. chars to move
std ; .. in backward direction
scrinit20: stosb ; save an attribute
movsb ; .. move constant data
loop scrinit20 ; .. until all moved
cld ; reset direction
pop di ; restore regs
pop si
pop dx
pop cx
pop bx
pop ax
ret ; return to caller
scrinit endp
; ----------------------------------------------------------------------
; terminate w/help message; dx -> intermediate message
; al not zero if help wanted
; ----------------------------------------------------------------------
die proc
push ax ; save help request
mov ah, 9 ; ah = print ASCII$ message
int 21h ; print the error message
pop ax ; restore help request
or al, al ; q. help wanted?
jz die10 ; a. no .. exit
mov dx, offset help ; dx -> help message
mov ah, 9 ; ah = print ASCII$ message
int 21h ; ... print the help
die10: mov ax, 4c01h ; die w/some notice
int 21h ; ... return to dos
die endp
align 16 ; line up on paragraph
mainlen label byte ; .. length of main
mainline ends
; ----------------------------------------------------------------------
; This is the spool interface module. This supports the device
; independence for the spooling media.
;
;
; General Returns
; carry flag = error
;
;
; Initialize Spool
; ax = 00h, function code
;
;
; Read Queue Record
; ax = 01h, fuction code
; cx = record number
; dx -> buffer
;
;
; Write Queue Record
; ax = 02h, fuction code
; cx = record number
; dx -> buffer
;
;
; Read Queue Pointer
; ax = 03h, fuction code
; cx = record number
; dx -> buffer
;
;
; Write Queue Pointer
; ax = 04h, fuction code
; cx = record number
; dx -> buffer
;
;
; Allocate a Queue Record
; ax = 05h, function code
;
; Returns
; cx = record number
;
;
; Commit File
; ax = 06h, function code
;
;
; Close Spool
; ax = 07h, function code
;
; ----------------------------------------------------------------------
diskq segment para public 'code' ; disk spool routines
assume cs:diskq ; called via far call
disklen dw diskend ; length of module
db 'D' ; module identifier
jmp short disk ; jump to disk routine
diskhandle dw 0 ; disk handle
closeq db 0 ; close queue needed
disktbl label byte ; function table
dw disk1 ; Initialize Spool
dw disk2 ; Read Queue Record
dw disk3 ; Write Queue Record
dw disk4 ; Read Queue Record
dw disk5 ; Write Queue Record
dw disk6 ; Allocate a Queue Record
dw disk7 ; Commit File
dw disk8 ; Close Spool
disk proc far ; disk spool interface
push si ; save registers
push bx
push dx
shl ax,1 ; ax = offset of routine
mov si, ax ; si = ...
mov ah, 62h ; get user's psp
int 21h ; call DOS
push bx ; save old psp
mov ah, 50h ; ah = store psp
mov bx, ds ; bx -> pcs's psp
int 21h ; call DOS
push cx ; save last register
mov bx, cs:diskhandle ; bx = file handle
jmp word ptr cs:disktbl[si] ; goto proper entry point
; --------------------------
disk1 label byte ; Initialize Spool
mov ax, 3d02h ; ax = open function code
lea dx, qfile ; dx -> file name
int 21h ; q. open ok?
jnc disk150 ; a. yes .. read header
disk110: mov ah, 3ch ; ah = create function code
lea dx, qfile ; dx -> file name
mov cx, 0 ; cx = normal file attribute
int 21h ; try to create the spool
jc disk130 ; die if error
mov cs:diskhandle, ax ; save disk handle
mov bx, ax ; bx = spool file handle
mov ah, 40h ; ax = write file function
mov cx, qhlen ; cx = length of queue hdr
mov dx, offset qhfirstf ; dx -> read buffer
int 21h ; call DOS to write record
jc disk130 ; die if error
jmp disk700 ; else.. commit the file
disk130: jmp disk9 ; ..return with status
disk150: mov cs:diskhandle, ax ; save disk handle
mov bx, ax ; bx = spool file's handle
xor cx, cx ; cx = record 0
call diskrec ; position file for que hdr
mov ah, 3fh ; ax = read file function
mov cx, 2 ; cx = get queue signature
mov dx, offset qheader ; dx -> read buffer
int 21h ; q. read header ok?
jc disk165 ; a. no .. close and create
cmp qhvalid, 0b0bfh ; q. valid queue header?
jne disk165 ; a. yes .. continue
disk160: mov ah, 3fh ; ax = read file function
mov cx, qhlen - 2 ; cx = length of queue hdr
mov dx, offset qhfirstf ; dx -> read buffer
int 21h ; q. read rest of header ok?
jnc disk170 ; a. yes .. continue
disk165: mov ah, 3eh ; ah = close file
int 21h ; .. close the file
jmp disk110 ; else .. error return
disk170: mov ax, 4202h ; ax = lseek to end of file
xor cx, cx ; cx = 0
mov dx, cx ; cx:dx = 0, lseek offset
int 21h ; call DOS
mov cx, qrlen ; cx = queue record length
div cx ; ax = nbr of records in que
mov nextqrnbr, ax ; save next nbr for allocate
jmp short disk9 ; ..and return to caller
; --------------------------
disk2 label byte ; Read Queue Record
call diskrec ; q. seek to the right rec?
jc disk9 ; a. no .. error return
disk210: mov ah, 3fh ; ah = read file function
jmp short disk310 ; ..continue w/common code
; --------------------------
disk3 label byte ; Write Queue Record
call diskrec ; q. seek to the right rec?
jc disk9 ; a. no .. error return
mov ah, 40h ; ah = write file function
disk310: mov cx, qrlen ; cx = length of queue rec
int 21h ; call DOS
jc disk9 ; ..if error, return immed.
cmp cs:closeq, 1 ; q. need to close?
je disk700 ; a. yes .. do a commit file
clc ; clear carry
jmp short disk9 ; ..and rtn w/proper status
; --------------------------
disk4 label byte ; Read Queue Pointer
call diskrec ; q. seek to the right rec?
jc disk9 ; a. no .. error return
mov ah, 3fh ; ah = read file function
jmp short disk520 ; ..continue w/common code
; --------------------------
disk5 label byte ; Write Queue Pointer
call diskrec ; q. seek to the right rec?
jc disk9 ; a. no .. error return
mov ah, 40h ; ah = write file function
disk520: mov cx, 2 ; cx = length of queue ptr
int 21h ; call DOS
jmp short disk9 ; ..return with status
; --------------------------
disk6 label byte ; Allocate a Queue Record
mov cx, nextqrnbr ; cx = next record nbr
inc nextqrnbr ; setup next record nbr
mov cs:closeq, 1 ; setup to close next write
clc ; clear carry flag
add sp, 2 ; skip returning cx
jmp short disk910 ; ..then return w/record nbr
; --------------------------
disk7 label byte ; Commit File
disk700: mov cs:closeq, 0 ; clear close needed flag
mov ah, 45h ; ah = duplicate file handle
int 21h ; call DOS
mov bx, ax ; bx = dup'd file handle
jmp short disk810 ; ..continue w/common code
; --------------------------
disk8 label byte ; Close Spool
xor cx, cx ; cx = record 0
call diskrec ; position file for que hdr
mov qhvalid, 0b0bfh ; make record valid
mov ah, 40h ; ax = write file function
mov cx, qhlen ; cx = length of queue hdr
mov dx, offset qheader ; dx -> read buffer
int 21h ; q. write header ok?
; jc disk9 ; a. no .. rtn w/error stat
disk810: mov ah, 3eh ; ah = close file handle fnc
int 21h ; call DOS
; jmp disk9 ; ..rtn to caller w/status
; --------------------------
; Return to caller w/status
disk9: pop cx ; restore register
disk910: pop bx ; bx = user's psp
pushf ; save flags
mov ah, 50h ; ah = reset user's psp
int 21h ; call DOS
popf ; restore flags
pop dx ; restore rest of registers
pop bx
pop si
ret ; rtn w/carry flag as status
disk endp
; ----------------------------------------------------------------------
; This routine takes the record number and positions the file for the
; next read/write.
;
; Entry
; bx = file handle
; cx = record number
;
; Returns
; carry flag = error
;
; ----------------------------------------------------------------------
diskrec proc
push cx ; save registers
push dx
mov ax, qrlen ; ax = queue record length
mul cx ; dx:ax = file record offset
mov cx, dx ; cx = most upper word
mov dx, ax ; cx:ax = offset in file
mov ax, 4200h ; ax = lseek function code
int 21h ; call DOS
pop dx ; restore registers
pop cx
ret
diskrec endp
align 16 ; boundary for next driver
diskend label byte
diskq ends
; ----------------------------------------------------------------------
; This is the conventional memory spool handler
;
; ----------------------------------------------------------------------
convq segment para public 'code' ; conventional memory spool
assume cs:convq ; called via far call
convlen dw convend ; length of module
db 'C' ; module identifier
jmp short conv ; jump to routine
convtbl label byte ; function table
dw conv1 ; Initialize Spool
dw conv2 ; Read Queue Record
dw conv3 ; Write Queue Record
dw conv4 ; Read Queue Record
dw conv5 ; Write Queue Record
dw conv6 ; Allocate a Queue Record
dw conv7 ; Commit File
dw conv8 ; Close Spool
conv proc far ; conv spool interface
push si ; save registers
push bx
push dx
push cx
shl ax,1 ; ax = offset of routine
mov si, ax ; si -> routine to goto
jmp word ptr cs:convtbl[si] ; goto proper routine
; --------------------------
conv1 label byte ; Initialize Spool
jmp short conv9 ; ..and return to caller
; --------------------------
conv2 label byte ; Read Queue Record
call convrec ; seek to the right "record"
mov cx, qrlen ; cx = length of queue rec
call convread ; "read" the record
jmp short conv9 ; ..and return to caller
; --------------------------
conv3 label byte ; Write Queue Record
call convrec ; seek to the right "record"
mov cx, qrlen ; cx = length of queue rec
call convwrite ; "write" the record
jmp short conv9 ; ..and return to caller
; --------------------------
conv4 label byte ; Read Queue Pointer
call convrec ; seek to the right "record"
mov cx, 2 ; cx = length of queue rec
call convread ; "read" the record
jmp short conv9 ; ..and return to caller
; --------------------------
conv5 label byte ; Write Queue Pointer
call convrec ; seek to the right "record"
mov cx, 2 ; cx = length of queue rec
call convwrite ; "write" the record
jmp short conv9 ; ..and return to caller
; --------------------------
conv6 label byte ; Allocate a Queue Record
mov cx, nextqrnbr ; cx = next record nbr
cmp cx, memqsize ; q. enough records?
jae conv610 ; a. yes .. return w/error
inc nextqrnbr ; setup next record nbr
clc ; clear carry flag
add sp, 2 ; skip returning cx
jmp short conv910 ; ..then return w/record nbr
conv610: stc ; set carry/error flag
pop cx ; restore registers
pop dx
pop bx
pop si
ret ; return w/carry flag
; --------------------------
conv7 label byte ; Commit File
; jmp conv9 ; ..rtn to caller w/status
; --------------------------
conv8 label byte ; Close Spool
; jmp conv9 ; ..rtn to caller w/status
; --------------------------
; Return to caller w/status
conv9: pop cx ; restore registers
conv910: pop dx
pop bx
pop si
clc ; clear carry flag
ret ; return w/carry flag
conv endp
; ----------------------------------------------------------------------
; This routine takes the record number and positions the file for the
; next read/write.
;
; Entry
; cx = record number
;
; Returns
; bx -> record offset
;
; ----------------------------------------------------------------------
convrec proc
push cx ; save registers
push dx
mov ax, qrlen ; ax = queue record length
dec cx ; cx = zero-based rec number
mul cx ; dx:ax = file record offset
mov bx, ax ; bx -> this record
pop dx ; restore registers
pop cx
ret
convrec endp
; ----------------------------------------------------------------------
; This routine "reads" a record from the spool (conventional memory)
;
; Entry
; bx -> spool record
; cx = length
; ds:dx -> caller's record
;
; ----------------------------------------------------------------------
convread proc
push cx ; save registers
push si
push di
push es
push ds
mov si, bx ; si -> spool record
mov di, dx ; di -> user's buffer
push ds ; make ..
pop es ; es:di -> user's buffer
mov ds, memqseg ; ds:si -> spool record
cld ; .. the right way
rep movsb ; move the record
pop ds ; restore registers
pop es
pop di
pop si
pop cx
ret
convread endp
; ----------------------------------------------------------------------
; This routine "writes" a record to the spool (conventional memory)
;
; Entry
; bx -> spool record
; cx = length
; ds:dx -> caller's record
;
; ----------------------------------------------------------------------
convwrite proc
push cx ; save registers
push si
push di
push es
push ds
mov si, dx ; si -> user's record
mov di, bx ; di -> spool buffer
mov es, memqseg ; es:di -> spool record
cld ; .. the write way
rep movsb ; move the record
pop ds ; restore registers
pop es
pop di
pop si
pop cx
ret
convwrite endp
align 16 ; boundary for next driver
convend label byte
convq ends
end begin